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"
|
import "print.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
|
|
||||||
|
|
||||||
|
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._
|
||||||
import aqua.model.func.Call
|
import aqua.model.func.Call
|
||||||
import aqua.model.func.body._
|
import aqua.model.func.resolved._
|
||||||
import aqua.types.StreamType
|
import aqua.types.StreamType
|
||||||
import cats.Eval
|
import cats.Eval
|
||||||
import cats.data.Chain
|
import cats.data.Chain
|
||||||
import cats.free.Cofree
|
import cats.free.Cofree
|
||||||
import wvlet.log.Logger
|
import wvlet.log.LogSupport
|
||||||
|
|
||||||
sealed trait AirGen {
|
sealed trait AirGen {
|
||||||
def generate: Air
|
def generate: Air
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object AirGen {
|
object AirGen extends LogSupport {
|
||||||
|
|
||||||
private val logger = Logger.of[AirGen.type]
|
|
||||||
import logger._
|
|
||||||
|
|
||||||
def lambdaToString(ls: List[LambdaModel]): String = ls match {
|
def lambdaToString(ls: List[LambdaModel]): String = ls match {
|
||||||
case Nil => ""
|
case Nil => ""
|
||||||
@ -46,22 +43,27 @@ object AirGen {
|
|||||||
case list => list.reduceLeft(SeqGen)
|
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 {
|
op match {
|
||||||
case mt: MetaTag =>
|
// case mt: MetaTag =>
|
||||||
folder(mt.op, ops).map(ag => mt.comment.fold(ag)(CommentGen(_, ag)))
|
// folder(mt.op, ops).map(ag => mt.comment.fold(ag)(CommentGen(_, ag)))
|
||||||
case SeqTag =>
|
case SeqRes =>
|
||||||
Eval later ops.toList.reduceLeftOption(SeqGen).getOrElse(NullGen)
|
Eval later ops.toList.reduceLeftOption(SeqGen).getOrElse(NullGen)
|
||||||
case ParTag =>
|
case ParRes =>
|
||||||
Eval later ops.toList.reduceLeftOption(ParGen).getOrElse(NullGen)
|
Eval later ops.toList.reduceLeftOption(ParGen).getOrElse(NullGen)
|
||||||
case XorTag =>
|
case XorRes =>
|
||||||
Eval later ops.toList.reduceLeftOption(XorGen).getOrElse(NullGen)
|
Eval later (ops.toList match {
|
||||||
case XorTag.LeftBiased =>
|
case o :: Nil => XorGen(o, NullGen)
|
||||||
Eval later XorGen(opsToSingle(ops), 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)
|
Eval later NextGen(item)
|
||||||
case MatchMismatchTag(left, right, shouldMatch) =>
|
case MatchMismatchRes(left, right, shouldMatch) =>
|
||||||
Eval later MatchMismatchGen(
|
Eval later MatchMismatchGen(
|
||||||
valueToData(left),
|
valueToData(left),
|
||||||
valueToData(right),
|
valueToData(right),
|
||||||
@ -69,12 +71,12 @@ object AirGen {
|
|||||||
opsToSingle(ops)
|
opsToSingle(ops)
|
||||||
)
|
)
|
||||||
|
|
||||||
case ForTag(item, iterable) =>
|
case FoldRes(item, iterable) =>
|
||||||
Eval later ForGen(valueToData(iterable), item, opsToSingle(ops))
|
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(
|
Eval.later(
|
||||||
ServiceCallGen(
|
ServiceCallGen(
|
||||||
peerId.map(valueToData).getOrElse(DataView.InitPeerId),
|
valueToData(peerId),
|
||||||
valueToData(serviceId),
|
valueToData(serviceId),
|
||||||
funcName,
|
funcName,
|
||||||
args.map(valueToData),
|
args.map(valueToData),
|
||||||
@ -85,35 +87,14 @@ object AirGen {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
case CallArrowTag(funcName, _) =>
|
case _: NoAir =>
|
||||||
// TODO: should be already resolved & removed from tree
|
|
||||||
error(
|
|
||||||
s"Unresolved arrow in AirGen: $funcName"
|
|
||||||
)
|
|
||||||
Eval later NullGen
|
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
|
Cofree
|
||||||
.cata[Chain, OpTag, AirGen](op)(folder)
|
.cata[Chain, ResolvedOp, AirGen](op)(folder)
|
||||||
.value
|
.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ val airframeLog = "org.wvlet.airframe" %% "airframe-log" % airframeLogV
|
|||||||
name := "aqua-hll"
|
name := "aqua-hll"
|
||||||
|
|
||||||
val commons = Seq(
|
val commons = Seq(
|
||||||
baseAquaVersion := "0.1.6",
|
baseAquaVersion := "0.1.7",
|
||||||
version := baseAquaVersion.value + "-" + sys.env.getOrElse("BUILD_NUMBER", "SNAPSHOT"),
|
version := baseAquaVersion.value + "-" + sys.env.getOrElse("BUILD_NUMBER", "SNAPSHOT"),
|
||||||
scalaVersion := dottyVersion,
|
scalaVersion := dottyVersion,
|
||||||
libraryDependencies ++= Seq(
|
libraryDependencies ++= Seq(
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package aqua.model
|
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.model.func.{ArgsCall, FuncCallable, FuncModel}
|
||||||
import aqua.types.{ProductType, Type}
|
import aqua.types.{ProductType, Type}
|
||||||
import cats.Monoid
|
import cats.Monoid
|
||||||
@ -8,6 +8,7 @@ import cats.data.NonEmptyMap
|
|||||||
import cats.syntax.apply._
|
import cats.syntax.apply._
|
||||||
import cats.syntax.functor._
|
import cats.syntax.functor._
|
||||||
import cats.syntax.monoid._
|
import cats.syntax.monoid._
|
||||||
|
import wvlet.log.LogSupport
|
||||||
|
|
||||||
import scala.collection.immutable.SortedMap
|
import scala.collection.immutable.SortedMap
|
||||||
|
|
||||||
@ -64,7 +65,7 @@ case class AquaContext(
|
|||||||
.map(ProductType(name, _))
|
.map(ProductType(name, _))
|
||||||
}
|
}
|
||||||
|
|
||||||
object AquaContext {
|
object AquaContext extends LogSupport {
|
||||||
|
|
||||||
trait Implicits {
|
trait Implicits {
|
||||||
implicit val aquaContextMonoid: Monoid[AquaContext]
|
implicit val aquaContextMonoid: Monoid[AquaContext]
|
||||||
@ -99,7 +100,7 @@ object AquaContext {
|
|||||||
FuncCallable(
|
FuncCallable(
|
||||||
fnName,
|
fnName,
|
||||||
// TODO: capture ability resolution, get ID from the call context
|
// TODO: capture ability resolution, get ID from the call context
|
||||||
FuncOp.leaf(CallServiceTag(serviceId, fnName, call, None)),
|
FuncOp.leaf(CallServiceTag(serviceId, fnName, call)),
|
||||||
args,
|
args,
|
||||||
(ret.map(_.model), arrowType.res).mapN(_ -> _),
|
(ret.map(_.model), arrowType.res).mapN(_ -> _),
|
||||||
Map.empty,
|
Map.empty,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package aqua.model
|
package aqua.model
|
||||||
|
|
||||||
import aqua.model.func.body.FuncOp
|
import aqua.model.func.raw.FuncOp
|
||||||
import cats.kernel.Semigroup
|
import cats.kernel.Semigroup
|
||||||
|
|
||||||
trait Model
|
trait Model
|
||||||
|
@ -18,10 +18,18 @@ object ValueModel {
|
|||||||
implicit object ValueModelEq extends Eq[ValueModel] {
|
implicit object ValueModelEq extends Eq[ValueModel] {
|
||||||
override def eqv(x: ValueModel, y: ValueModel): Boolean = x == y
|
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 {
|
case class LiteralModel(value: String, `type`: Type) extends ValueModel {
|
||||||
override def lastType: Type = `type`
|
override def lastType: Type = `type`
|
||||||
|
|
||||||
|
override def toString: String = s"{$value: ${`type`}}"
|
||||||
}
|
}
|
||||||
|
|
||||||
object LiteralModel {
|
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.data.Chain
|
||||||
import cats.free.Cofree
|
import cats.free.Cofree
|
||||||
@ -24,17 +24,36 @@ case class ChainZipper[T](prev: Chain[T], current: T, next: Chain[T]) {
|
|||||||
object ChainZipper {
|
object ChainZipper {
|
||||||
def one[T](el: T): ChainZipper[T] = ChainZipper(Chain.empty, el, Chain.empty)
|
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]] =
|
def first[T](chain: Chain[T]): Option[ChainZipper[T]] =
|
||||||
chain.uncons.fold(Chain.empty[ChainZipper[T]]) { case (t, next) =>
|
chain.uncons.map { case (current, next) =>
|
||||||
ChainZipper(prev, t, next) +: fromChain(next, prev :+ t)
|
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]
|
f: ChainZipper[T] => Option[T]
|
||||||
): Chain[T] =
|
): Chain[T] =
|
||||||
chain.uncons.fold(Chain.empty[T]) { case (t, next) =>
|
chain.uncons.fold(Chain.empty[T]) { case (t, next) =>
|
||||||
f(ChainZipper(prev, t, next)).fold(fromChainMap(next, prev)(f))(r =>
|
f(ChainZipper(prev, t, next)).fold(traverseChainMap(next, prev)(f))(r =>
|
||||||
r +: fromChainMap(next, prev :+ r)(f)
|
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, _, _) =>
|
def argVarNames: Set[String] = args.collect { case VarModel(name, _, _) =>
|
||||||
name
|
name
|
||||||
}.toSet
|
}.toSet
|
||||||
|
|
||||||
|
override def toString: String =
|
||||||
|
s"[${args.mkString(" ")}]${exportTo.map(_.model).map(" " + _).getOrElse("")}"
|
||||||
}
|
}
|
||||||
|
|
||||||
object Call {
|
object Call {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package aqua.model.func
|
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.model.{Model, ValueModel, VarModel}
|
||||||
import aqua.types.{ArrowType, Type}
|
import aqua.types.{ArrowType, Type}
|
||||||
import cats.Eval
|
import cats.Eval
|
||||||
@ -86,7 +86,7 @@ case class FuncCallable(
|
|||||||
(
|
(
|
||||||
noNames,
|
noNames,
|
||||||
resolvedExports + (assignTo -> value.resolveWith(resolvedExports))
|
resolvedExports + (assignTo -> value.resolveWith(resolvedExports))
|
||||||
) -> Cofree[Chain, OpTag](
|
) -> Cofree[Chain, RawTag](
|
||||||
tag.mapValues(_.resolveWith(resolvedExports)),
|
tag.mapValues(_.resolveWith(resolvedExports)),
|
||||||
Eval.now(Chain.empty)
|
Eval.now(Chain.empty)
|
||||||
)
|
)
|
||||||
@ -119,7 +119,7 @@ case class FuncCallable(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// All the other tags are already resolved and need no substitution
|
// All the other tags are already resolved and need no substitution
|
||||||
acc -> Cofree[Chain, OpTag](
|
acc -> Cofree[Chain, RawTag](
|
||||||
tag.mapValues(_.resolveWith(resolvedExports)),
|
tag.mapValues(_.resolveWith(resolvedExports)),
|
||||||
Eval.now(Chain.empty)
|
Eval.now(Chain.empty)
|
||||||
)
|
)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package aqua.model.func
|
package aqua.model.func
|
||||||
|
|
||||||
import aqua.model.func.body.FuncOp
|
import aqua.model.func.raw.FuncOp
|
||||||
import aqua.model.{Model, ValueModel}
|
import aqua.model.{Model, ValueModel}
|
||||||
import aqua.types.Type
|
import aqua.types.Type
|
||||||
|
|
||||||
@ -15,6 +15,6 @@ case class FuncModel(
|
|||||||
arrows: Map[String, FuncCallable],
|
arrows: Map[String, FuncCallable],
|
||||||
constants: Map[String, ValueModel]
|
constants: Map[String, ValueModel]
|
||||||
): FuncCallable =
|
): 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.func.Call
|
||||||
import aqua.model.{Model, ValueModel, VarModel}
|
import aqua.model.{Model, ValueModel, VarModel}
|
||||||
@ -9,8 +9,8 @@ import cats.kernel.Semigroup
|
|||||||
import cats.syntax.apply._
|
import cats.syntax.apply._
|
||||||
import cats.syntax.functor._
|
import cats.syntax.functor._
|
||||||
|
|
||||||
case class FuncOp(tree: Cofree[Chain, OpTag]) extends Model {
|
case class FuncOp(tree: Cofree[Chain, RawTag]) extends Model {
|
||||||
def head: OpTag = tree.head
|
def head: RawTag = tree.head
|
||||||
|
|
||||||
lazy val isRightAssoc: Boolean = head match {
|
lazy val isRightAssoc: Boolean = head match {
|
||||||
case XorTag | ParTag => true
|
case XorTag | ParTag => true
|
||||||
@ -18,13 +18,13 @@ case class FuncOp(tree: Cofree[Chain, OpTag]) extends Model {
|
|||||||
case _ => false
|
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)
|
Cofree.cata(tree)(folder)
|
||||||
|
|
||||||
def definesVarNames: Eval[Set[String]] = cata[Set[String]] {
|
def definesVarNames: Eval[Set[String]] = cata[Set[String]] {
|
||||||
case (CallArrowTag(_, Call(_, Some(export))), acc) =>
|
case (CallArrowTag(_, Call(_, Some(export))), acc) =>
|
||||||
Eval.later(acc.foldLeft(Set(export.name))(_ ++ _))
|
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))(_ ++ _))
|
Eval.later(acc.foldLeft(Set(export.name))(_ ++ _))
|
||||||
case (NextTag(export), acc) => Eval.later(acc.foldLeft(Set(export))(_ ++ _))
|
case (NextTag(export), acc) => Eval.later(acc.foldLeft(Set(export))(_ ++ _))
|
||||||
case (_, acc) => Eval.later(acc.foldLeft(Set.empty[String])(_ ++ _))
|
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]] {
|
def exportsVarNames: Eval[Set[String]] = cata[Set[String]] {
|
||||||
case (CallArrowTag(_, Call(_, Some(export))), acc) =>
|
case (CallArrowTag(_, Call(_, Some(export))), acc) =>
|
||||||
Eval.later(acc.foldLeft(Set(export.name))(_ ++ _))
|
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))(_ ++ _))
|
Eval.later(acc.foldLeft(Set(export.name))(_ ++ _))
|
||||||
case (_, acc) => Eval.later(acc.foldLeft(Set.empty[String])(_ ++ _))
|
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]] {
|
def usesVarNames: Eval[Set[String]] = cata[Set[String]] {
|
||||||
case (CallArrowTag(_, call), acc) =>
|
case (CallArrowTag(_, call), acc) =>
|
||||||
Eval.later(acc.foldLeft(call.argVarNames)(_ ++ _))
|
Eval.later(acc.foldLeft(call.argVarNames)(_ ++ _))
|
||||||
case (CallServiceTag(_, _, call, _), acc) =>
|
case (CallServiceTag(_, _, call), acc) =>
|
||||||
Eval.later(acc.foldLeft(call.argVarNames)(_ ++ _))
|
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])(_ ++ _))
|
case (_, acc) => Eval.later(acc.foldLeft(Set.empty[String])(_ ++ _))
|
||||||
}
|
}
|
||||||
|
|
||||||
def resolveValues(vals: Map[String, ValueModel]): FuncOp =
|
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 =
|
def rename(vals: Map[String, String]): FuncOp =
|
||||||
FuncOp(
|
FuncOp(
|
||||||
tree.map[OpTag](op =>
|
tree.map[RawTag](op =>
|
||||||
op.mapValues {
|
op.mapValues {
|
||||||
case v: VarModel if vals.contains(v.name) => v.copy(name = vals(v.name))
|
case v: VarModel if vals.contains(v.name) => v.copy(name = vals(v.name))
|
||||||
case v => v
|
case v => v
|
||||||
@ -68,13 +73,29 @@ case class FuncOp(tree: Cofree[Chain, OpTag]) extends Model {
|
|||||||
|
|
||||||
def :+:(prev: FuncOp): FuncOp =
|
def :+:(prev: FuncOp): FuncOp =
|
||||||
FuncOp.RightAssocSemi.combine(prev, this)
|
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 {
|
object FuncOp {
|
||||||
type Tree = Cofree[Chain, OpTag]
|
type Tree = Cofree[Chain, RawTag]
|
||||||
|
|
||||||
def traverseA[A](cf: Tree, init: A)(
|
def traverseA[A](cf: Tree, init: A)(
|
||||||
f: (A, OpTag) => (A, Tree)
|
f: (A, RawTag) => (A, Tree)
|
||||||
): Eval[(A, Tree)] = {
|
): Eval[(A, Tree)] = {
|
||||||
val (headA, head) = f(init, cf.head)
|
val (headA, head) = f(init, cf.head)
|
||||||
// TODO: it should be in standard library, with some other types
|
// 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(_ ++ _)))
|
FuncOp(y.tree.copy(tail = (x.tree.tail, y.tree.tail).mapN(_ ++ _)))
|
||||||
case (XorTag.LeftBiased, XorTag) =>
|
case (XorTag.LeftBiased, XorTag) =>
|
||||||
wrap(SeqTag, FuncOp(y.tree.copy(tail = (x.tree.tail, y.tree.tail).mapN(_ ++ _))))
|
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) =>
|
case (_, ParTag | XorTag) =>
|
||||||
wrap(SeqTag, FuncOp(y.tree.copy(tail = y.tree.tail.map(_.prepend(x.tree)))))
|
wrap(SeqTag, FuncOp(y.tree.copy(tail = y.tree.tail.map(_.prepend(x.tree)))))
|
||||||
case (_, XorParTag(xor, par)) =>
|
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 =
|
def node(tag: RawTag, children: Chain[FuncOp]): FuncOp =
|
||||||
FuncOp(Cofree[Chain, OpTag](tag, Eval.later(children.map(_.tree))))
|
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.func.Call
|
||||||
import aqua.model.{LiteralModel, ValueModel}
|
import aqua.model.{LiteralModel, ValueModel}
|
||||||
@ -7,12 +7,12 @@ import cats.free.Cofree
|
|||||||
|
|
||||||
object FuncOps {
|
object FuncOps {
|
||||||
|
|
||||||
def noop(peerId: ValueModel): FuncOp =
|
def noop: FuncOp =
|
||||||
FuncOp.leaf(CallServiceTag(LiteralModel.quote("op"), "identity", Call(Nil, None), Some(peerId)))
|
FuncOp.leaf(CallServiceTag(LiteralModel.quote("op"), "identity", Call(Nil, None)))
|
||||||
|
|
||||||
def identity(what: ValueModel, to: Call.Export): FuncOp =
|
def identity(what: ValueModel, to: Call.Export): FuncOp =
|
||||||
FuncOp.leaf(
|
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 =
|
def callService(srvId: ValueModel, funcName: String, call: Call): FuncOp =
|
||||||
@ -51,6 +51,19 @@ object FuncOps {
|
|||||||
.map(FuncOp(_))
|
.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 =
|
def xor(left: FuncOp, right: FuncOp): FuncOp =
|
||||||
FuncOp.node(XorTag, Chain(left, right))
|
FuncOp.node(XorTag, Chain(left, right))
|
||||||
|
|
||||||
@ -62,16 +75,4 @@ object FuncOps {
|
|||||||
|
|
||||||
def next(item: String): FuncOp =
|
def next(item: String): FuncOp =
|
||||||
FuncOp.leaf(NextTag(item))
|
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.ValueModel
|
||||||
import aqua.model.func.Call
|
import aqua.model.func.Call
|
||||||
import cats.data.Chain
|
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 OnTag(peerId, via) => OnTag(f(peerId), via.map(f))
|
||||||
case MatchMismatchTag(left, right, shouldMatch) =>
|
case MatchMismatchTag(left, right, shouldMatch) =>
|
||||||
MatchMismatchTag(f(left), f(right), shouldMatch)
|
MatchMismatchTag(f(left), f(right), shouldMatch)
|
||||||
@ -16,12 +16,11 @@ sealed trait OpTag {
|
|||||||
funcName,
|
funcName,
|
||||||
call.mapValues(f)
|
call.mapValues(f)
|
||||||
)
|
)
|
||||||
case CallServiceTag(serviceId, funcName, call, pid) =>
|
case CallServiceTag(serviceId, funcName, call) =>
|
||||||
CallServiceTag(
|
CallServiceTag(
|
||||||
f(serviceId),
|
f(serviceId),
|
||||||
funcName,
|
funcName,
|
||||||
call.mapValues(f),
|
call.mapValues(f)
|
||||||
pid.map(f)
|
|
||||||
)
|
)
|
||||||
case AssignmentTag(value, assignTo) =>
|
case AssignmentTag(value, assignTo) =>
|
||||||
AssignmentTag(f(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
|
sealed trait SeqGroupTag extends GroupTag
|
||||||
|
|
||||||
case object SeqTag extends SeqGroupTag
|
case object SeqTag extends SeqGroupTag
|
||||||
case object ParTag extends GroupTag
|
case object ParTag extends GroupTag
|
||||||
|
|
||||||
case object XorTag extends GroupTag {
|
case object XorTag extends SeqGroupTag {
|
||||||
case object LeftBiased extends GroupTag
|
case object LeftBiased extends GroupTag
|
||||||
}
|
}
|
||||||
case class XorParTag(xor: FuncOp, par: FuncOp) extends OpTag
|
case class XorParTag(xor: FuncOp, par: FuncOp) extends RawTag
|
||||||
case class OnTag(peerId: ValueModel, via: Chain[ValueModel]) extends SeqGroupTag
|
|
||||||
case class NextTag(item: String) extends OpTag
|
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)
|
case class MatchMismatchTag(left: ValueModel, right: ValueModel, shouldMatch: Boolean)
|
||||||
extends SeqGroupTag
|
extends SeqGroupTag
|
||||||
case class ForTag(item: String, iterable: ValueModel) 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(
|
case class CallArrowTag(
|
||||||
funcName: String,
|
funcName: String,
|
||||||
call: Call
|
call: Call
|
||||||
) extends OpTag
|
) extends RawTag
|
||||||
|
|
||||||
case class AssignmentTag(
|
case class AssignmentTag(
|
||||||
value: ValueModel,
|
value: ValueModel,
|
||||||
assignTo: String
|
assignTo: String
|
||||||
) extends NoAirTag
|
) extends NoExecTag
|
||||||
|
|
||||||
case class AbilityIdTag(
|
case class AbilityIdTag(
|
||||||
value: ValueModel,
|
value: ValueModel,
|
||||||
service: String
|
service: String
|
||||||
) extends NoAirTag
|
) extends NoExecTag
|
||||||
|
|
||||||
case class CallServiceTag(
|
case class CallServiceTag(
|
||||||
serviceId: ValueModel,
|
serviceId: ValueModel,
|
||||||
funcName: String,
|
funcName: String,
|
||||||
call: Call,
|
call: Call
|
||||||
peerId: Option[ValueModel] = None
|
) extends RawTag {
|
||||||
) extends OpTag
|
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.Show
|
||||||
import cats.free.Cofree
|
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
|
package aqua.model.topology
|
||||||
|
|
||||||
import aqua.model.{ValueModel, VarModel}
|
import aqua.model.{LiteralModel, ValueModel, VarModel}
|
||||||
import aqua.model.func.body._
|
import aqua.model.func.raw._
|
||||||
import cats.Eval
|
import cats.Eval
|
||||||
import cats.data.Chain
|
import cats.data.{Chain, NonEmptyChain, NonEmptyList, OptionT}
|
||||||
import cats.data.Chain.{:==, ==:, nil}
|
import cats.data.Chain.nil
|
||||||
import cats.free.Cofree
|
import cats.free.Cofree
|
||||||
import ChainZipper.Matchers._
|
import aqua.model.cursor.ChainZipper
|
||||||
import Location.Matchers._
|
import aqua.model.func.resolved._
|
||||||
import aqua.types.{BoxType, ScalarType}
|
import aqua.types.{BoxType, ScalarType}
|
||||||
import wvlet.log.LogSupport
|
import wvlet.log.LogSupport
|
||||||
|
import cats.syntax.traverse._
|
||||||
import scala.annotation.tailrec
|
|
||||||
|
|
||||||
object Topology extends LogSupport {
|
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
|
Cofree
|
||||||
.cata[Chain, OpTag, Tree](resolveOnMoves(op)) {
|
.cata[Chain, ResolvedOp, Res](resolveOnMoves(op).value) {
|
||||||
case (SeqTag | _: OnTag | MetaTag(false, _, SeqTag | _: OnTag), children) =>
|
case (SeqRes, children) =>
|
||||||
Eval.later(
|
Eval.later(
|
||||||
Cofree(
|
children.uncons
|
||||||
SeqTag,
|
.filter(_._2.isEmpty)
|
||||||
Eval.now(children.flatMap {
|
.map(_._1)
|
||||||
case Cofree(SeqTag, ch) => ch.value
|
.getOrElse(
|
||||||
case cf => Chain.one(cf)
|
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)))
|
case (head, children) => Eval.later(Cofree(head, Eval.now(children)))
|
||||||
}
|
}
|
||||||
.value
|
.value
|
||||||
|
|
||||||
def resolveOnMoves(op: Tree): Tree =
|
def wrap(cz: ChainZipper[Res]): Chain[Res] =
|
||||||
Cursor
|
Chain.one(
|
||||||
.transform(op)(transformWalker)
|
if (cz.prev.nonEmpty || cz.next.nonEmpty) Cofree(SeqRes, Eval.now(cz.chain))
|
||||||
.getOrElse(op)
|
else cz.current
|
||||||
|
|
||||||
@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
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Need to get from there
|
def resolveOnMoves(op: Tree): Eval[Res] =
|
||||||
val prevPeerId =
|
RawCursor(NonEmptyList.one(ChainZipper.one(op)))
|
||||||
Chain.fromOption(prevOn.lastOption.map(_.peerId) orElse c.loc.firstOn.map(_.peerId))
|
.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)
|
.map(NonEmptyChain.fromChain(_).map(_.uncons))
|
||||||
skipSuffix(noPrefix, suffix, noPrefix)
|
.map {
|
||||||
}
|
case None =>
|
||||||
|
error("Topology emitted nothing")
|
||||||
def findPath(
|
Cofree(SeqRes, MakeRes.nilTail)
|
||||||
fromOn: Chain[OnTag],
|
case Some((el, `nil`)) => el
|
||||||
toOn: Chain[OnTag],
|
case Some((el, tail)) =>
|
||||||
fromPeer: Chain[ValueModel],
|
warn("Topology emitted many nodes, that's unusual")
|
||||||
toPeer: Chain[ValueModel]
|
Cofree(SeqRes, Eval.now(el +: tail))
|
||||||
): 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Walks through peer IDs, doing a noop function on each
|
// Walks through peer IDs, doing a noop function on each
|
||||||
// If same IDs are found in a row, does noop only once
|
// 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
|
// 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 =>
|
peerIds.map { v =>
|
||||||
v.lastType match {
|
v.lastType match {
|
||||||
case _: BoxType =>
|
case _: BoxType =>
|
||||||
val itemName = "-via-peer-"
|
val itemName = "-via-peer-"
|
||||||
|
|
||||||
FuncOps.meta(
|
MakeRes.fold(
|
||||||
FuncOps.fold(
|
itemName,
|
||||||
itemName,
|
v,
|
||||||
v,
|
if (reversed)
|
||||||
if (reversed)
|
MakeRes.seq(
|
||||||
FuncOps.seq(
|
MakeRes.next(itemName),
|
||||||
FuncOps.next(itemName),
|
MakeRes.noop(VarModel(itemName, ScalarType.string))
|
||||||
FuncOps.noop(VarModel(itemName, ScalarType.string))
|
)
|
||||||
)
|
else
|
||||||
else
|
MakeRes.seq(
|
||||||
FuncOps.seq(
|
MakeRes.noop(VarModel(itemName, ScalarType.string)),
|
||||||
FuncOps.noop(VarModel(itemName, ScalarType.string)),
|
MakeRes.next(itemName)
|
||||||
FuncOps.next(itemName)
|
)
|
||||||
)
|
|
||||||
),
|
|
||||||
skipTopology = true
|
|
||||||
)
|
)
|
||||||
case _ =>
|
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.{ValueModel, VarModel}
|
||||||
import aqua.model.func.Call
|
import aqua.model.func.Call
|
||||||
import aqua.model.func.body.{FuncOp, FuncOps}
|
import aqua.model.func.raw.{FuncOp, FuncOps}
|
||||||
import aqua.types.{ArrayType, DataType, OptionType, StreamType, Type}
|
import aqua.types.{ArrayType, DataType, StreamType}
|
||||||
import cats.data.Chain
|
import cats.data.Chain
|
||||||
|
|
||||||
trait ArgsProvider {
|
trait ArgsProvider {
|
||||||
@ -26,7 +26,6 @@ case class ArgsFromService(dataServiceId: ValueModel, names: List[(String, DataT
|
|||||||
item,
|
item,
|
||||||
VarModel(iter, ArrayType(t.element), Chain.empty),
|
VarModel(iter, ArrayType(t.element), Chain.empty),
|
||||||
FuncOps.seq(
|
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.identity(VarModel(item, t.element), Call.Export(name, t)),
|
||||||
FuncOps.next(item)
|
FuncOps.next(item)
|
||||||
)
|
)
|
||||||
|
@ -2,7 +2,7 @@ package aqua.model.transform
|
|||||||
|
|
||||||
import aqua.model.{LiteralModel, ValueModel, VarModel}
|
import aqua.model.{LiteralModel, ValueModel, VarModel}
|
||||||
import aqua.model.func.Call
|
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 aqua.types.LiteralType
|
||||||
import cats.Eval
|
import cats.Eval
|
||||||
import cats.data.Chain
|
import cats.data.Chain
|
||||||
@ -19,7 +19,7 @@ case class ErrorsCatcher(
|
|||||||
if (enabled) {
|
if (enabled) {
|
||||||
var i = 0
|
var i = 0
|
||||||
op
|
op
|
||||||
.cata[Cofree[Chain, OpTag]] {
|
.cata[Cofree[Chain, RawTag]] {
|
||||||
case (tag, children)
|
case (tag, children)
|
||||||
if children.length == 1 && children.headOption.exists(
|
if children.length == 1 && children.headOption.exists(
|
||||||
_.head == XorTag.LeftBiased
|
_.head == XorTag.LeftBiased
|
||||||
|
@ -2,7 +2,7 @@ package aqua.model.transform
|
|||||||
|
|
||||||
import aqua.model.{LiteralModel, ValueModel}
|
import aqua.model.{LiteralModel, ValueModel}
|
||||||
import aqua.model.func.Call
|
import aqua.model.func.Call
|
||||||
import aqua.model.func.body.{FuncOp, FuncOps}
|
import aqua.model.func.raw.{FuncOp, FuncOps}
|
||||||
import cats.data.Chain
|
import cats.data.Chain
|
||||||
|
|
||||||
sealed trait InitPeerCallable {
|
sealed trait InitPeerCallable {
|
||||||
|
@ -2,7 +2,7 @@ package aqua.model.transform
|
|||||||
|
|
||||||
import aqua.model.{ValueModel, VarModel}
|
import aqua.model.{ValueModel, VarModel}
|
||||||
import aqua.model.func.{ArgDef, ArgsCall, ArgsDef, Call, FuncCallable}
|
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 aqua.types.ArrowType
|
||||||
import cats.Eval
|
import cats.Eval
|
||||||
import cats.syntax.apply._
|
import cats.syntax.apply._
|
||||||
|
@ -1,27 +1,28 @@
|
|||||||
package aqua.model.transform
|
package aqua.model.transform
|
||||||
|
|
||||||
import aqua.model.func.body._
|
|
||||||
import aqua.model.func.FuncCallable
|
import aqua.model.func.FuncCallable
|
||||||
import aqua.model.VarModel
|
import aqua.model.VarModel
|
||||||
|
import aqua.model.func.resolved.{NoAir, ResolvedOp}
|
||||||
import aqua.model.topology.Topology
|
import aqua.model.topology.Topology
|
||||||
import aqua.types.ScalarType
|
import aqua.types.ScalarType
|
||||||
import cats.data.Chain
|
import cats.data.Chain
|
||||||
import cats.free.Cofree
|
import cats.free.Cofree
|
||||||
|
import wvlet.log.LogSupport
|
||||||
|
|
||||||
object Transform {
|
object Transform extends LogSupport {
|
||||||
|
|
||||||
def defaultFilter(t: OpTag): Boolean = t match {
|
def defaultFilter(t: ResolvedOp): Boolean = t match {
|
||||||
case _: NoAirTag => false
|
case _: NoAir => false
|
||||||
case _ => true
|
case _ => true
|
||||||
}
|
}
|
||||||
|
|
||||||
def clear(
|
def clear(
|
||||||
tree: Cofree[Chain, OpTag],
|
tree: Cofree[Chain, ResolvedOp],
|
||||||
filter: OpTag => Boolean = defaultFilter
|
filter: ResolvedOp => Boolean = defaultFilter
|
||||||
): Cofree[Chain, OpTag] =
|
): Cofree[Chain, ResolvedOp] =
|
||||||
tree.copy(tail = tree.tail.map(_.filter(t => filter(t.head)).map(clear(_, filter))))
|
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(
|
val initCallable: InitPeerCallable = InitViaRelayCallable(
|
||||||
Chain.fromOption(conf.relayVarName).map(VarModel(_, ScalarType.string))
|
Chain.fromOption(conf.relayVarName).map(VarModel(_, ScalarType.string))
|
||||||
)
|
)
|
||||||
|
@ -130,8 +130,8 @@ object Expr {
|
|||||||
case block if block.isBlock =>
|
case block if block.isBlock =>
|
||||||
acc.copy(block = Some(indent -> currentExpr))
|
acc.copy(block = Some(indent -> currentExpr))
|
||||||
// create leaf if token is on current level
|
// create leaf if token is on current level
|
||||||
case e =>
|
case _ =>
|
||||||
acc.copy(currentChildren = acc.currentChildren.append(leaf(e)))
|
acc.copy(currentChildren = acc.currentChildren.append(currentExpr))
|
||||||
}
|
}
|
||||||
// if we have root companion, gather all expressions that have indent > than current
|
// if we have root companion, gather all expressions that have indent > than current
|
||||||
case r @ Some((_, block)) =>
|
case r @ Some((_, block)) =>
|
||||||
@ -163,10 +163,10 @@ object Expr {
|
|||||||
window = Chain.empty
|
window = Chain.empty
|
||||||
)
|
)
|
||||||
// create leaf if token is on current level
|
// create leaf if token is on current level
|
||||||
case e =>
|
case _ =>
|
||||||
acc.copy(
|
acc.copy(
|
||||||
block = None,
|
block = None,
|
||||||
currentChildren = withTree.append(leaf(e)),
|
currentChildren = withTree.append(currentExpr),
|
||||||
window = Chain.empty
|
window = Chain.empty
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -267,4 +267,14 @@ class FuncExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
|||||||
qGenComplex.d() shouldBe elseCall
|
qGenComplex.d() shouldBe elseCall
|
||||||
qGenComplex.d() shouldBe CallServiceTag(local, "gt", Call(List(), Some("three")), None)*/
|
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
|
package aqua.semantics
|
||||||
|
|
||||||
import aqua.model.{AquaContext, Model, ScriptModel}
|
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.lexer.Token
|
||||||
import aqua.parser.{Ast, Expr}
|
import aqua.parser.{Ast, Expr}
|
||||||
import aqua.semantics.rules.ReportError
|
import aqua.semantics.rules.ReportError
|
||||||
@ -23,8 +23,9 @@ import monocle.Lens
|
|||||||
import monocle.macros.GenLens
|
import monocle.macros.GenLens
|
||||||
import cats.syntax.apply._
|
import cats.syntax.apply._
|
||||||
import cats.syntax.semigroup._
|
import cats.syntax.semigroup._
|
||||||
|
import wvlet.log.LogSupport
|
||||||
|
|
||||||
object Semantics {
|
object Semantics extends LogSupport {
|
||||||
|
|
||||||
def folder[F[_], G[_]](implicit
|
def folder[F[_], G[_]](implicit
|
||||||
A: AbilitiesAlgebra[F, G],
|
A: AbilitiesAlgebra[F, G],
|
||||||
@ -91,7 +92,7 @@ object Semantics {
|
|||||||
.run(CompilerState.init[F](init))
|
.run(CompilerState.init[F](init))
|
||||||
.map {
|
.map {
|
||||||
case (state, gen: ScriptModel) =>
|
case (state, gen: ScriptModel) =>
|
||||||
val ctx = AquaContext.fromScriptModel(gen, aqum.empty)
|
val ctx = AquaContext.fromScriptModel(gen, init)
|
||||||
NonEmptyChain
|
NonEmptyChain
|
||||||
.fromChain(state.errors)
|
.fromChain(state.errors)
|
||||||
.fold[ValidatedNec[SemanticError[F], AquaContext]](Valid(ctx))(Invalid(_))
|
.fold[ValidatedNec[SemanticError[F], AquaContext]](Valid(ctx))(Invalid(_))
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package aqua.semantics.expr
|
package aqua.semantics.expr
|
||||||
|
|
||||||
import aqua.model.Model
|
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.parser.expr.AbilityIdExpr
|
||||||
import aqua.semantics.Prog
|
import aqua.semantics.Prog
|
||||||
import aqua.semantics.rules.ValuesAlgebra
|
import aqua.semantics.rules.ValuesAlgebra
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package aqua.semantics.expr
|
package aqua.semantics.expr
|
||||||
|
|
||||||
import aqua.model.Model
|
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.parser.expr.{AbilityIdExpr, AssignmentExpr}
|
||||||
import aqua.semantics.Prog
|
import aqua.semantics.Prog
|
||||||
import aqua.semantics.rules.ValuesAlgebra
|
import aqua.semantics.rules.ValuesAlgebra
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package aqua.semantics.expr
|
package aqua.semantics.expr
|
||||||
|
|
||||||
import aqua.model.func.Call
|
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.model.{Model, ValueModel}
|
||||||
import aqua.parser.expr.CallArrowExpr
|
import aqua.parser.expr.CallArrowExpr
|
||||||
import aqua.semantics.Prog
|
import aqua.semantics.Prog
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package aqua.semantics.expr
|
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.model.{Model, VarModel}
|
||||||
import aqua.parser.expr.CatchExpr
|
import aqua.parser.expr.CatchExpr
|
||||||
import aqua.semantics.Prog
|
import aqua.semantics.Prog
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package aqua.semantics.expr
|
package aqua.semantics.expr
|
||||||
|
|
||||||
import aqua.model.Model
|
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.parser.expr.ElseOtherwiseExpr
|
||||||
import aqua.semantics.Prog
|
import aqua.semantics.Prog
|
||||||
import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package aqua.semantics.expr
|
package aqua.semantics.expr
|
||||||
|
|
||||||
import aqua.model.{Model, ValueModel}
|
import aqua.model.{Model, ValueModel}
|
||||||
import aqua.model.func.body._
|
import aqua.model.func.raw._
|
||||||
import aqua.parser.expr.ForExpr
|
import aqua.parser.expr.ForExpr
|
||||||
import aqua.semantics.Prog
|
import aqua.semantics.Prog
|
||||||
import aqua.semantics.rules.ValuesAlgebra
|
import aqua.semantics.rules.ValuesAlgebra
|
||||||
@ -41,7 +41,7 @@ class ForSem[F[_]](val expr: ForExpr[F]) extends AnyVal {
|
|||||||
FuncOp.wrap(
|
FuncOp.wrap(
|
||||||
ForTag(expr.item.value, vm),
|
ForTag(expr.item.value, vm),
|
||||||
FuncOp.node(
|
FuncOp.node(
|
||||||
expr.mode.map(_._2).fold[OpTag](SeqTag) {
|
expr.mode.map(_._2).fold[RawTag](SeqTag) {
|
||||||
case ForExpr.ParMode => ParTag
|
case ForExpr.ParMode => ParTag
|
||||||
case ForExpr.TryMode => XorTag
|
case ForExpr.TryMode => XorTag
|
||||||
},
|
},
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package aqua.semantics.expr
|
package aqua.semantics.expr
|
||||||
|
|
||||||
import aqua.model.func.body.FuncOp
|
import aqua.model.func.raw.FuncOp
|
||||||
import aqua.model.{Model, ValueModel}
|
import aqua.model.{Model, ValueModel}
|
||||||
import aqua.model.func.{ArgDef, ArgsDef, FuncModel}
|
import aqua.model.func.{ArgDef, ArgsDef, FuncModel}
|
||||||
import aqua.parser.expr.FuncExpr
|
import aqua.parser.expr.FuncExpr
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package aqua.semantics.expr
|
package aqua.semantics.expr
|
||||||
|
|
||||||
import aqua.model.{Model, ValueModel}
|
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.parser.expr.IfExpr
|
||||||
import aqua.semantics.rules.ValuesAlgebra
|
import aqua.semantics.rules.ValuesAlgebra
|
||||||
import aqua.semantics.rules.types.TypesAlgebra
|
import aqua.semantics.rules.types.TypesAlgebra
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package aqua.semantics.expr
|
package aqua.semantics.expr
|
||||||
|
|
||||||
import aqua.model.{Model, ValueModel}
|
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.parser.expr.OnExpr
|
||||||
import aqua.semantics.Prog
|
import aqua.semantics.Prog
|
||||||
import aqua.semantics.rules.ValuesAlgebra
|
import aqua.semantics.rules.ValuesAlgebra
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package aqua.semantics.expr
|
package aqua.semantics.expr
|
||||||
|
|
||||||
import aqua.model.Model
|
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.parser.expr.ParExpr
|
||||||
import aqua.semantics.Prog
|
import aqua.semantics.Prog
|
||||||
import cats.free.Free
|
import cats.free.Free
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package aqua.semantics.expr
|
package aqua.semantics.expr
|
||||||
|
|
||||||
import aqua.model.Model
|
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.parser.expr.TryExpr
|
||||||
import aqua.semantics.Prog
|
import aqua.semantics.Prog
|
||||||
import aqua.semantics.rules.ValuesAlgebra
|
import aqua.semantics.rules.ValuesAlgebra
|
||||||
|
@ -2,6 +2,7 @@ package aqua.semantics
|
|||||||
|
|
||||||
import aqua.Node
|
import aqua.Node
|
||||||
import aqua.Node._
|
import aqua.Node._
|
||||||
|
import aqua.model.func.raw.{FuncOp, FuncOps, SeqTag}
|
||||||
import aqua.model.transform._
|
import aqua.model.transform._
|
||||||
import aqua.model.{AquaContext, LiteralModel}
|
import aqua.model.{AquaContext, LiteralModel}
|
||||||
import aqua.parser.Ast
|
import aqua.parser.Ast
|
||||||
@ -13,7 +14,7 @@ import org.scalatest.matchers.should.Matchers
|
|||||||
class SemanticsSpec extends AnyFlatSpec with Matchers {
|
class SemanticsSpec extends AnyFlatSpec with Matchers {
|
||||||
|
|
||||||
// use it to fix https://github.com/fluencelabs/aqua/issues/90
|
// 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
|
implicit val fileLift: LiftParser[Span.F] = Span.spanLiftParser
|
||||||
|
|
||||||
val script =
|
val script =
|
||||||
@ -37,15 +38,16 @@ class SemanticsSpec extends AnyFlatSpec with Matchers {
|
|||||||
|
|
||||||
val proc = Node.cofToNode(func.body.tree)
|
val proc = Node.cofToNode(func.body.tree)
|
||||||
|
|
||||||
val expected =
|
val expected: Node.Raw =
|
||||||
seq(
|
FuncOp.wrap(
|
||||||
par(
|
SeqTag,
|
||||||
on(LiteralModel("\"other-peer\"", LiteralType.string), Nil, callLiteral(1)),
|
FuncOps.par(
|
||||||
callLiteral(1)
|
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
|
package aqua
|
||||||
|
|
||||||
import aqua.model.func.Call
|
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.transform.{BodyConfig, ErrorsCatcher}
|
||||||
import aqua.model.{LiteralModel, ValueModel, VarModel}
|
import aqua.model.{LiteralModel, ValueModel, VarModel}
|
||||||
import aqua.types.{ArrayType, LiteralType, ScalarType}
|
import aqua.types.{ArrayType, LiteralType, ScalarType}
|
||||||
@ -12,15 +13,143 @@ import cats.free.Cofree
|
|||||||
import scala.language.implicitConversions
|
import scala.language.implicitConversions
|
||||||
|
|
||||||
// Helper to simplify building and visualizing Cofree structures
|
// 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 =
|
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)
|
def equalsOrPrintDiff[TT](other: Node[TT]): Boolean =
|
||||||
Console.GREEN + left + Console.RESET
|
if (this == other) true
|
||||||
else
|
else {
|
||||||
Console.BLUE + left + Console.RED + " != " + Console.YELLOW + right)
|
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 =
|
private def diffArg(left: ValueModel, right: ValueModel): String =
|
||||||
Console.GREEN + "(" +
|
Console.GREEN + "(" +
|
||||||
@ -36,129 +165,53 @@ case class Node(tag: OpTag, ops: List[Node] = Nil) {
|
|||||||
.mkString("::") + Console.GREEN + ", " +
|
.mkString("::") + Console.GREEN + ", " +
|
||||||
equalOrNot(left.exportTo, right.exportTo) + Console.GREEN + ")"
|
equalOrNot(left.exportTo, right.exportTo) + Console.GREEN + ")"
|
||||||
|
|
||||||
private def diffServiceCall(left: CallServiceTag, right: CallServiceTag): String =
|
private def diffServiceCall(left: CallServiceRes, right: CallServiceRes): String =
|
||||||
Console.GREEN + "CallServiceTag(" +
|
Console.GREEN + "(call" +
|
||||||
equalOrNot(left.serviceId, right.serviceId) + Console.GREEN + ", " +
|
equalOrNot(left.peerId, right.peerId) + Console.GREEN + " (" +
|
||||||
equalOrNot(left.funcName, right.funcName) + Console.GREEN + ", " +
|
equalOrNot(left.serviceId, right.serviceId) + Console.GREEN + " " +
|
||||||
diffCall(left.call, right.call) + Console.GREEN + ", " +
|
equalOrNot(left.funcName, right.funcName) + Console.GREEN + ") " +
|
||||||
equalOrNot(left.peerId, right.peerId) +
|
diffCall(left.call, right.call) + Console.GREEN +
|
||||||
Console.GREEN + ")" + Console.RESET
|
Console.GREEN + ")" + Console.RESET
|
||||||
|
|
||||||
private def diffTags(left: OpTag, right: OpTag): String = (left, right) match {
|
private def diffRes(left: ResolvedOp, right: ResolvedOp): String = (left, right) match {
|
||||||
case (l: CallServiceTag, r: CallServiceTag) => diffServiceCall(l, r)
|
case (l: CallServiceRes, r: CallServiceRes) => diffServiceCall(l, r)
|
||||||
case _ =>
|
case _ =>
|
||||||
Console.BLUE + s" $left ${Console.RED}\n != ${Console.YELLOW}${right}${Console.RED}"
|
Console.BLUE + s" $left ${Console.RED}\n != ${Console.YELLOW}${right}${Console.RED}"
|
||||||
}
|
}
|
||||||
|
|
||||||
def diffToString(other: Node): String =
|
def diffToString(current: Node.Res, other: Node.Res): String =
|
||||||
(if (tag == other.tag) Console.GREEN + tag
|
(if (current.label == other.label) Console.GREEN + current.label
|
||||||
else diffTags(tag, other.tag)) + (if (ops.isEmpty && other.ops.isEmpty) "\n"
|
else
|
||||||
else
|
diffRes(current.label, other.label)) + (if (
|
||||||
"{\n") + Console.RESET +
|
current.children.isEmpty && other.children.isEmpty
|
||||||
(if (ops.length != other.ops.length)
|
) "\n"
|
||||||
Console.RED + s"number of ops: ${ops.length} != ${other.ops.length}\n" + Console.RESET
|
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 "") +
|
else "") +
|
||||||
ops
|
current.children
|
||||||
.zip(other.ops)
|
.map(Option(_))
|
||||||
.map { case (a, b) =>
|
.zipAll(other.children.map(Option(_)), None, None)
|
||||||
a.diffToString(b)
|
.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
|
else
|
||||||
((if (tag == other.tag) Console.GREEN
|
((if (current.label == other.label) Console.GREEN
|
||||||
else Console.RED) + "}\n" + Console.RESET))
|
else Console.RED) + "}\n" + Console.RESET))
|
||||||
|
|
||||||
def equalsOrPrintDiff(other: Node): Boolean =
|
def equalsOrPrintDiff(current: Node.Res, other: Node.Res): Boolean =
|
||||||
if (this == other) true
|
if (current == other) true
|
||||||
else {
|
else {
|
||||||
println(diffToString(other))
|
println(diffToString(current, other))
|
||||||
false
|
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
|
package aqua.model.topology
|
||||||
|
|
||||||
import aqua.Node
|
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.flatspec.AnyFlatSpec
|
||||||
import org.scalatest.matchers.should.Matchers
|
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 {
|
"topology resolver" should "do nothing on init peer" in {
|
||||||
|
|
||||||
val init = on(
|
val init: Node.Raw = on(
|
||||||
initPeer,
|
initPeer,
|
||||||
relay :: Nil,
|
relay :: Nil,
|
||||||
seq(
|
FuncOps.seq(
|
||||||
call(1),
|
callTag(1),
|
||||||
call(2)
|
callTag(2)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val proc: Node = Topology.resolve(init)
|
val proc: Node.Res = Topology.resolve(init)
|
||||||
|
|
||||||
val expected =
|
val expected: Node.Res =
|
||||||
seq(
|
MakeRes.seq(
|
||||||
call(1, initPeer),
|
callRes(1, initPeer),
|
||||||
call(2, initPeer)
|
callRes(2, initPeer)
|
||||||
)
|
)
|
||||||
|
|
||||||
proc should be(expected)
|
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 {
|
"topology resolver" should "go through relay to any other node, directly" in {
|
||||||
|
|
||||||
val init = on(
|
val init: Node.Raw = on(
|
||||||
initPeer,
|
initPeer,
|
||||||
relay :: Nil,
|
relay :: Nil,
|
||||||
on(
|
on(
|
||||||
otherPeer,
|
otherPeer,
|
||||||
Nil,
|
Nil,
|
||||||
seq(
|
FuncOps.seq(
|
||||||
call(1),
|
callTag(1),
|
||||||
call(2)
|
callTag(2)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val proc: Node = Topology.resolve(init)
|
val proc: Node.Res = Topology.resolve(init)
|
||||||
|
|
||||||
val expected =
|
val expected: Node.Res =
|
||||||
seq(through(relay), call(1, otherPeer), call(2, otherPeer))
|
MakeRes.seq(through(relay), callRes(1, otherPeer), callRes(2, otherPeer))
|
||||||
|
|
||||||
proc should be(expected)
|
proc should be(expected)
|
||||||
}
|
}
|
||||||
@ -61,21 +69,139 @@ class TopologySpec extends AnyFlatSpec with Matchers {
|
|||||||
on(
|
on(
|
||||||
otherPeer,
|
otherPeer,
|
||||||
otherRelay :: Nil,
|
otherRelay :: Nil,
|
||||||
seq(
|
FuncOps.seq(
|
||||||
call(1),
|
callTag(1),
|
||||||
call(2)
|
callTag(2)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val proc: Node = Topology.resolve(init)
|
val proc: Node.Res = Topology.resolve(init)
|
||||||
|
|
||||||
val expected =
|
val expected: Node.Res =
|
||||||
seq(
|
MakeRes.seq(
|
||||||
through(relay),
|
through(relay),
|
||||||
through(otherRelay),
|
through(otherRelay),
|
||||||
call(1, otherPeer),
|
callRes(1, otherPeer),
|
||||||
call(2, 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)
|
proc.equalsOrPrintDiff(expected) should be(true)
|
||||||
@ -89,28 +215,28 @@ class TopologySpec extends AnyFlatSpec with Matchers {
|
|||||||
on(
|
on(
|
||||||
otherPeer,
|
otherPeer,
|
||||||
otherRelay :: Nil,
|
otherRelay :: Nil,
|
||||||
xor(
|
FuncOps.xor(
|
||||||
seq(
|
FuncOps.seq(
|
||||||
call(1),
|
callTag(1),
|
||||||
call(2)
|
callTag(2)
|
||||||
),
|
),
|
||||||
call(3)
|
callTag(3)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val proc: Node = Topology.resolve(init)
|
val proc: Node.Res = Topology.resolve(init)
|
||||||
|
|
||||||
val expected =
|
val expected: Node.Res =
|
||||||
seq(
|
MakeRes.seq(
|
||||||
through(relay),
|
through(relay),
|
||||||
through(otherRelay),
|
through(otherRelay),
|
||||||
xor(
|
MakeRes.xor(
|
||||||
seq(
|
MakeRes.seq(
|
||||||
call(1, otherPeer),
|
callRes(1, otherPeer),
|
||||||
call(2, 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 {
|
"topology resolver" should "simplify a route with init_peer_id" in {
|
||||||
val init = on(
|
val init: Node.Raw = on(
|
||||||
initPeer,
|
initPeer,
|
||||||
relay :: Nil,
|
relay :: Nil,
|
||||||
seq(
|
FuncOps.seq(
|
||||||
on(
|
on(
|
||||||
initPeer,
|
initPeer,
|
||||||
relay :: Nil,
|
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 =
|
val expected =
|
||||||
seq(
|
MakeRes.seq(
|
||||||
call(1, initPeer),
|
callRes(1, initPeer),
|
||||||
call(2, initPeer)
|
callRes(2, initPeer)
|
||||||
)
|
)
|
||||||
|
|
||||||
proc.equalsOrPrintDiff(expected) should be(true)
|
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 {
|
"topology resolver" should "get back to init peer" in {
|
||||||
|
|
||||||
val init = on(
|
val init: Node.Raw = on(
|
||||||
initPeer,
|
initPeer,
|
||||||
relay :: Nil,
|
relay :: Nil,
|
||||||
seq(
|
FuncOps.seq(
|
||||||
on(
|
on(
|
||||||
otherPeer,
|
otherPeer,
|
||||||
otherRelay :: Nil,
|
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 =
|
val expected: Node.Res =
|
||||||
seq(
|
MakeRes.seq(
|
||||||
through(relay),
|
through(relay),
|
||||||
through(otherRelay),
|
through(otherRelay),
|
||||||
call(1, otherPeer),
|
callRes(1, otherPeer),
|
||||||
through(otherRelay),
|
through(otherRelay),
|
||||||
through(relay),
|
through(relay),
|
||||||
call(2, initPeer)
|
callRes(2, initPeer)
|
||||||
)
|
)
|
||||||
|
|
||||||
// println(Console.BLUE + init)
|
// println(Console.BLUE + init)
|
||||||
@ -197,19 +323,19 @@ class TopologySpec extends AnyFlatSpec with Matchers {
|
|||||||
val init = on(
|
val init = on(
|
||||||
initPeer,
|
initPeer,
|
||||||
relay :: Nil,
|
relay :: Nil,
|
||||||
seq(
|
FuncOps.seq(
|
||||||
call(1),
|
callTag(1),
|
||||||
call(2),
|
callTag(2),
|
||||||
call(3),
|
callTag(3),
|
||||||
on(
|
on(
|
||||||
varNode,
|
varNode,
|
||||||
viaList :: Nil,
|
viaList :: Nil,
|
||||||
call(4)
|
callTag(4)
|
||||||
),
|
),
|
||||||
on(
|
on(
|
||||||
initPeer,
|
initPeer,
|
||||||
relay :: Nil,
|
relay :: Nil,
|
||||||
call(5)
|
callTag(5)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -222,50 +348,50 @@ class TopologySpec extends AnyFlatSpec with Matchers {
|
|||||||
val init = on(
|
val init = on(
|
||||||
initPeer,
|
initPeer,
|
||||||
relay :: Nil,
|
relay :: Nil,
|
||||||
seq(
|
FuncOps.seq(
|
||||||
on(
|
on(
|
||||||
otherPeer,
|
otherPeer,
|
||||||
otherRelay :: Nil,
|
otherRelay :: Nil,
|
||||||
call(0),
|
callTag(0),
|
||||||
on(
|
on(
|
||||||
otherPeer2,
|
otherPeer2,
|
||||||
otherRelay :: Nil,
|
otherRelay :: Nil,
|
||||||
call(1),
|
callTag(1),
|
||||||
_match(
|
matchRaw(
|
||||||
otherPeer,
|
otherPeer,
|
||||||
otherRelay,
|
otherRelay,
|
||||||
on(
|
on(
|
||||||
otherPeer,
|
otherPeer,
|
||||||
otherRelay :: Nil,
|
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 =
|
val expected: Node.Res =
|
||||||
seq(
|
MakeRes.seq(
|
||||||
through(relay),
|
through(relay),
|
||||||
through(otherRelay),
|
through(otherRelay),
|
||||||
call(0, otherPeer),
|
callRes(0, otherPeer),
|
||||||
through(otherRelay),
|
through(otherRelay),
|
||||||
call(1, otherPeer2),
|
callRes(1, otherPeer2),
|
||||||
_match(
|
matchRes(
|
||||||
otherPeer,
|
otherPeer,
|
||||||
otherRelay,
|
otherRelay,
|
||||||
seq(
|
MakeRes.seq(
|
||||||
through(otherRelay),
|
through(otherRelay),
|
||||||
call(2, otherPeer)
|
callRes(2, otherPeer)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
through(otherRelay),
|
through(otherRelay),
|
||||||
through(relay),
|
through(relay),
|
||||||
call(3, initPeer)
|
callRes(3, initPeer)
|
||||||
)
|
)
|
||||||
|
|
||||||
// println(Console.BLUE + init)
|
// println(Console.BLUE + init)
|
||||||
@ -276,4 +402,63 @@ class TopologySpec extends AnyFlatSpec with Matchers {
|
|||||||
proc.equalsOrPrintDiff(expected) should be(true)
|
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
|
package aqua.model.transform
|
||||||
|
|
||||||
import aqua.Node
|
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.func.{ArgsDef, Call, FuncCallable}
|
||||||
import aqua.model.{LiteralModel, VarModel}
|
import aqua.model.{LiteralModel, VarModel}
|
||||||
import aqua.types.ScalarType
|
import aqua.types.ScalarType
|
||||||
@ -11,14 +12,14 @@ import org.scalatest.matchers.should.Matchers
|
|||||||
class TransformSpec extends AnyFlatSpec with Matchers {
|
class TransformSpec extends AnyFlatSpec with Matchers {
|
||||||
import Node._
|
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 ret = LiteralModel.quote("return this")
|
||||||
|
|
||||||
val func: FuncCallable =
|
val func: FuncCallable =
|
||||||
FuncCallable(
|
FuncCallable(
|
||||||
"ret",
|
"ret",
|
||||||
FuncOp(on(otherPeer, otherRelay :: Nil, call(1))),
|
on(otherPeer, otherRelay :: Nil, callTag(1)),
|
||||||
ArgsDef.empty,
|
ArgsDef.empty,
|
||||||
Some((ret, ScalarType.string)),
|
Some((ret, ScalarType.string)),
|
||||||
Map.empty,
|
Map.empty,
|
||||||
@ -29,17 +30,17 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
|||||||
|
|
||||||
val fc = Transform.forClient(func, bc)
|
val fc = Transform.forClient(func, bc)
|
||||||
|
|
||||||
val procFC: Node = fc
|
val procFC: Node.Res = fc
|
||||||
|
|
||||||
val expectedFC = seq(
|
val expectedFC: Node.Res =
|
||||||
xor(
|
MakeRes.xor(
|
||||||
seq(
|
MakeRes.seq(
|
||||||
dataCall(bc, "-relay-", initPeer),
|
dataCall(bc, "-relay-", initPeer),
|
||||||
through(relayV),
|
through(relayV),
|
||||||
through(otherRelay),
|
through(otherRelay),
|
||||||
xor(
|
MakeRes.xor(
|
||||||
call(1, otherPeer),
|
callRes(1, otherPeer),
|
||||||
seq(
|
MakeRes.seq(
|
||||||
through(otherRelay),
|
through(otherRelay),
|
||||||
through(relayV),
|
through(relayV),
|
||||||
errorCall(bc, 1, initPeer),
|
errorCall(bc, 1, initPeer),
|
||||||
@ -48,22 +49,17 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
|||||||
),
|
),
|
||||||
through(otherRelay),
|
through(otherRelay),
|
||||||
through(relayV),
|
through(relayV),
|
||||||
xor(
|
MakeRes.xor(
|
||||||
respCall(bc, ret, initPeer),
|
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(
|
val func: FuncCallable = FuncCallable(
|
||||||
"ret",
|
"ret",
|
||||||
FuncOp(seq(call(0), on(otherPeer, Nil, call(1)))),
|
FuncOps.seq(callTag(0), on(otherPeer, Nil, callTag(1))),
|
||||||
ArgsDef.empty,
|
ArgsDef.empty,
|
||||||
Some((ret, ScalarType.string)),
|
Some((ret, ScalarType.string)),
|
||||||
Map.empty,
|
Map.empty,
|
||||||
@ -84,14 +80,14 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
|||||||
|
|
||||||
val fc = Transform.forClient(func, bc)
|
val fc = Transform.forClient(func, bc)
|
||||||
|
|
||||||
val procFC: Node = fc
|
val procFC: Res = fc
|
||||||
|
|
||||||
val expectedFC =
|
val expectedFC: Res =
|
||||||
seq(
|
MakeRes.seq(
|
||||||
dataCall(bc, "-relay-", initPeer),
|
dataCall(bc, "-relay-", initPeer),
|
||||||
call(0, initPeer),
|
callRes(0, initPeer),
|
||||||
through(relayV),
|
through(relayV),
|
||||||
call(1, otherPeer),
|
callRes(1, otherPeer),
|
||||||
through(relayV),
|
through(relayV),
|
||||||
respCall(bc, ret, initPeer)
|
respCall(bc, ret, initPeer)
|
||||||
)
|
)
|
||||||
@ -119,10 +115,9 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
|||||||
CallServiceTag(
|
CallServiceTag(
|
||||||
LiteralModel.quote("srv1"),
|
LiteralModel.quote("srv1"),
|
||||||
"foo",
|
"foo",
|
||||||
Call(Nil, Some(Call.Export("v", ScalarType.string))),
|
Call(Nil, Some(Call.Export("v", ScalarType.string)))
|
||||||
None
|
|
||||||
)
|
)
|
||||||
)
|
).cof
|
||||||
),
|
),
|
||||||
ArgsDef.empty,
|
ArgsDef.empty,
|
||||||
Some((VarModel("v", ScalarType.string), ScalarType.string)),
|
Some((VarModel("v", ScalarType.string), ScalarType.string)),
|
||||||
@ -134,7 +129,7 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
|||||||
FuncCallable(
|
FuncCallable(
|
||||||
"f2",
|
"f2",
|
||||||
FuncOp(
|
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,
|
ArgsDef.empty,
|
||||||
Some((VarModel("v", ScalarType.string), ScalarType.string)),
|
Some((VarModel("v", ScalarType.string), ScalarType.string)),
|
||||||
@ -144,17 +139,17 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
|||||||
|
|
||||||
val bc = BodyConfig(wrapWithXor = false)
|
val bc = BodyConfig(wrapWithXor = false)
|
||||||
|
|
||||||
val res = Transform.forClient(f2, bc): Node
|
val res = Transform.forClient(f2, bc): Node.Res
|
||||||
|
|
||||||
res.equalsOrPrintDiff(
|
res.equalsOrPrintDiff(
|
||||||
seq(
|
MakeRes.seq(
|
||||||
dataCall(bc, "-relay-", initPeer),
|
dataCall(bc, "-relay-", initPeer),
|
||||||
Node(
|
Node(
|
||||||
CallServiceTag(
|
CallServiceRes(
|
||||||
LiteralModel.quote("srv1"),
|
LiteralModel.quote("srv1"),
|
||||||
"foo",
|
"foo",
|
||||||
Call(Nil, Some(Call.Export("v", ScalarType.string))),
|
Call(Nil, Some(Call.Export("v", ScalarType.string))),
|
||||||
Some(initPeer)
|
initPeer
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
respCall(bc, VarModel("v", ScalarType.string), initPeer)
|
respCall(bc, VarModel("v", ScalarType.string), initPeer)
|
||||||
|
@ -16,7 +16,7 @@ sealed trait Type {
|
|||||||
sealed trait DataType extends Type
|
sealed trait DataType extends Type
|
||||||
|
|
||||||
case class ScalarType private (name: String) extends DataType {
|
case class ScalarType private (name: String) extends DataType {
|
||||||
override def toString: String = s"ScalarType($name)"
|
override def toString: String = name
|
||||||
}
|
}
|
||||||
|
|
||||||
object ScalarType {
|
object ScalarType {
|
||||||
@ -72,7 +72,7 @@ object ScalarType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case class LiteralType private (oneOf: Set[ScalarType], name: String) extends DataType {
|
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 {
|
object LiteralType {
|
||||||
|
Loading…
Reference in New Issue
Block a user