Fix via path, complex tests (#27)

This commit is contained in:
Dima 2021-04-05 10:40:51 +03:00 committed by GitHub
parent cad921a958
commit 992af16a08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 323 additions and 118 deletions

View File

@ -2,7 +2,7 @@ version = 2.7.5
docstrings = JavaDoc
maxColumn = 120
maxColumn = 100
align = none
align {

View File

@ -11,13 +11,15 @@ object Aqua {
def parse(input: String): ValidatedNec[AquaError, Ast[Span.F]] =
Ast.fromString[Span.F](input)
def validate(input: String): ValidatedNec[AquaError, Model] =
parse(input).andThen(ast => Semantics.validate(ast).leftMap(_.map(ts => CompilerError(ts._1.unit._1, ts._2))))
def generateModel(input: String): ValidatedNec[AquaError, Model] =
parse(input).andThen(ast =>
Semantics.generateModel(ast).leftMap(_.map(ts => CompilerError(ts._1.unit._1, ts._2)))
)
def generate(input: String, air: Boolean): ValidatedNec[AquaError, String] =
validate(input).map {
case g: ScriptModel =>
if (air) g.generateAir else g.generateTypescript
generateModel(input).map {
case m: ScriptModel =>
if (air) m.generateAir else m.generateTypescript
case _ => "//No input given"
}

View File

@ -50,7 +50,8 @@ case class FuncCallable(
// We have some names in scope (forbiddenNames), can't introduce them again; so find new names
val shouldRename = findNewNames(forbiddenNames, treeDefines)
// If there was a collision, rename exports and usages with new names
val treeRenamed = if (shouldRename.isEmpty) treeWithValues else treeWithValues.rename(shouldRename)
val treeRenamed =
if (shouldRename.isEmpty) treeWithValues else treeWithValues.rename(shouldRename)
// Result could be derived from arguments, or renamed; take care about that
val result = ret.map(_._1).map(_.resolveWith(argsToData)).map {
@ -71,7 +72,9 @@ case class FuncCallable(
case ((noNames, resolvedExports), CallArrowTag(None, fn, c)) if allArrows.contains(fn) =>
// Apply arguments to a function recursion
val (appliedOp, value) =
allArrows(fn).apply(c.mapValues(_.resolveWith(resolvedExports)), argsToArrows, noNames).value
allArrows(fn)
.apply(c.mapValues(_.resolveWith(resolvedExports)), argsToArrows, noNames)
.value
// Function defines new names inside its body need to collect them
// TODO: actually it's done and dropped so keep and pass it instead
@ -80,7 +83,10 @@ case class FuncCallable(
(noNames ++ newNames, resolvedExports ++ c.exportTo.zip(value)) -> appliedOp.tree
case (acc @ (_, resolvedExports), tag) =>
// All the other tags are already resolved and need no substitution
acc -> Cofree[Chain, OpTag](tag.mapValues(_.resolveWith(resolvedExports)), Eval.now(Chain.empty))
acc -> Cofree[Chain, OpTag](
tag.mapValues(_.resolveWith(resolvedExports)),
Eval.now(Chain.empty)
)
}
.map { case ((_, resolvedExports), b) =>
// If return value is affected by any of internal functions, resolve it
@ -159,13 +165,7 @@ case class FuncCallable(
).value._1
) ++ Chain.fromSeq(returnCallback.toSeq)
)
.resolveTopology(noop)
def noop(peerId: ValueModel): FuncOp =
FuncOp.wrap(
OnTag(peerId, Nil),
FuncOp.leaf(CallServiceTag(LiteralModel("\"op\""), "identity", Call(Nil, None), Some(peerId)))
)
.resolveTopology()
def generateTsCall: Call =
Call(

View File

@ -1,6 +1,5 @@
package aqua.model
import aqua.model.FuncOp.wrap
import cats.Eval
import cats.data.Chain
import cats.free.Cofree
@ -16,8 +15,10 @@ case class FuncOp(tree: Cofree[Chain, OpTag]) extends Model {
Cofree.cata(tree)(folder)
def definesValueNames: Eval[Set[String]] = cata[Set[String]] {
case (CallArrowTag(_, _, Call(_, Some(export))), acc) => Eval.later(acc.foldLeft(Set(export))(_ ++ _))
case (CallServiceTag(_, _, Call(_, Some(export)), _), acc) => Eval.later(acc.foldLeft(Set(export))(_ ++ _))
case (CallArrowTag(_, _, Call(_, Some(export))), acc) =>
Eval.later(acc.foldLeft(Set(export))(_ ++ _))
case (CallServiceTag(_, _, Call(_, Some(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])(_ ++ _))
}
@ -41,7 +42,7 @@ case class FuncOp(tree: Cofree[Chain, OpTag]) extends Model {
)
)
def resolveTopology(doVia: ValueModel => FuncOp): FuncOp =
def resolveTopology(): FuncOp =
FuncOp(FuncOp.transformWithPath(tree) {
case (path, c: CallServiceTag) =>
Cofree[Chain, OpTag](
@ -55,11 +56,12 @@ case class FuncOp(tree: Cofree[Chain, OpTag]) extends Model {
OnTag(pid, Nil),
Eval.now(
Chain.fromSeq(
via.map(doVia).map(_.tree)
via.map(FuncOp.noop).map(_.tree)
)
)
)
case (_, t) => Cofree[Chain, OpTag](t, Eval.now(Chain.empty))
case (_, t) =>
Cofree[Chain, OpTag](t, Eval.now(Chain.empty))
})
def :+:(prev: FuncOp): FuncOp =
@ -68,14 +70,21 @@ case class FuncOp(tree: Cofree[Chain, OpTag]) extends Model {
object FuncOp {
def noop(peerId: ValueModel): FuncOp =
FuncOp.wrap(
OnTag(peerId, Nil),
FuncOp.leaf(CallServiceTag(LiteralModel("\"op\""), "identity", Call(Nil, None), Some(peerId)))
)
def traverseA[A](cf: Cofree[Chain, OpTag], init: A)(
f: (A, OpTag) => (A, Cofree[Chain, OpTag])
): Eval[(A, Cofree[Chain, OpTag])] = {
val (headA, head) = f(init, cf.head)
// TODO: it should be in standard library, with some other types
cf.tail
.map(_.foldLeft[(A, Chain[Cofree[Chain, OpTag]])]((headA, head.tailForced)) { case ((aggrA, aggrTail), child) =>
traverseA(child, aggrA)(f).value.map(aggrTail.append)
.map(_.foldLeft[(A, Chain[Cofree[Chain, OpTag]])]((headA, head.tailForced)) {
case ((aggrA, aggrTail), child) =>
traverseA(child, aggrA)(f).value.map(aggrTail.append)
})
.map(_.map(ch => head.copy(tail = Eval.now(ch))))
}
@ -83,13 +92,13 @@ object FuncOp {
def transformWithPath(cf: Cofree[Chain, OpTag], path: List[OpTag] = Nil)(
f: (List[OpTag], OpTag) => Cofree[Chain, OpTag]
): Cofree[Chain, OpTag] = {
val h = f(path, cf.head)
val newCf = f(path, cf.head)
Cofree[Chain, OpTag](
h.head,
(h.tail, cf.tail)
newCf.head,
(newCf.tail, cf.tail)
.mapN(_ ++ _)
// IF make foldLeft here, will be possible to get info from prev sibling
.map(_.map(transformWithPath(_, h.head :: path)(f)))
.map(_.map(transformWithPath(_, newCf.head :: path)(f)))
)
}

View File

@ -4,7 +4,12 @@ import aqua.model.{FuncOp, Model}
import aqua.parser.lexer.Token
import aqua.parser.{Ast, Expr}
import aqua.semantics.rules.ReportError
import aqua.semantics.rules.abilities.{AbilitiesAlgebra, AbilitiesInterpreter, AbilitiesState, AbilityOp}
import aqua.semantics.rules.abilities.{
AbilitiesAlgebra,
AbilitiesInterpreter,
AbilitiesState,
AbilityOp
}
import aqua.semantics.rules.names.{NameOp, NamesAlgebra, NamesInterpreter, NamesState}
import aqua.semantics.rules.types.{TypeOp, TypesAlgebra, TypesInterpreter, TypesState}
import cats.Eval
@ -30,12 +35,13 @@ object Semantics {
// TODO instead of foldRight, do slidingWindow for 2 elements, merge right associative ones
// Then foldLeft just like now
inners
.foldRight[Free[G, List[Model]]](Free.pure[G, List[Model]](List.empty[Model])) { case (a, b) =>
(a, b).mapN {
case (prev: FuncOp, (next: FuncOp) :: tail) if next.isRightAssoc =>
(prev :+: next) :: tail
case (prev, acc) => prev :: acc
}
.foldRight[Free[G, List[Model]]](Free.pure[G, List[Model]](List.empty[Model])) {
case (a, b) =>
(a, b).mapN {
case (prev: FuncOp, (next: FuncOp) :: tail) if next.isRightAssoc =>
(prev :+: next) :: tail
case (prev, acc) => prev :: acc
}
}
.map(_.reduceLeftOption(_ |+| _).getOrElse(Model.empty("AST is empty")))
)
@ -58,13 +64,15 @@ object Semantics {
import monocle.macros.syntax.all._
implicit val re: ReportError[F, CompilerState[F]] =
(st: CompilerState[F], token: Token[F], hint: String) => st.focus(_.errors).modify(_.append(token -> hint))
(st: CompilerState[F], token: Token[F], hint: String) =>
st.focus(_.errors).modify(_.append(token -> hint))
implicit val ns: Lens[CompilerState[F], NamesState[F]] = GenLens[CompilerState[F]](_.names)
val names = new NamesInterpreter[F, CompilerState[F]]()
implicit val as: Lens[CompilerState[F], AbilitiesState[F]] = GenLens[CompilerState[F]](_.abilities)
implicit val as: Lens[CompilerState[F], AbilitiesState[F]] =
GenLens[CompilerState[F]](_.abilities)
val abilities = new AbilitiesInterpreter[F, CompilerState[F]]()
@ -78,11 +86,13 @@ object Semantics {
free.foldMap[State[CompilerState[F], *]](interpreter)
}
def validate[F[_]](ast: Ast[F]): ValidatedNec[(Token[F], String), Model] =
def generateModel[F[_]](ast: Ast[F]): ValidatedNec[(Token[F], String), Model] =
(transpile[F] _ andThen interpret[F])(ast)
.run(CompilerState[F]())
.map { case (state, gen) =>
NonEmptyChain.fromChain(state.errors).fold[ValidatedNec[(Token[F], String), Model]](Valid(gen))(Invalid(_))
NonEmptyChain
.fromChain(state.errors)
.fold[ValidatedNec[(Token[F], String), Model]](Valid(gen))(Invalid(_))
}
.value
}

View File

@ -5,6 +5,7 @@ import aqua.parser.expr.OnExpr
import aqua.semantics.Prog
import aqua.semantics.rules.ValuesAlgebra
import aqua.semantics.rules.abilities.AbilitiesAlgebra
import cats.data.Chain
import cats.syntax.flatMap._
import cats.syntax.functor._
@ -24,7 +25,30 @@ class OnSem[F[_]](val expr: OnExpr[F]) extends AnyVal {
(_: Unit, ops: Model) =>
A.endScope() as (ops match {
case op: FuncOp =>
FuncOp.wrap(OnTag(ValuesAlgebra.valueToModel(expr.peerId), expr.via.map(ValuesAlgebra.valueToModel)), op)
// the way to the node may be lost if there will be chains of `on` without calls
// so peerId is added to the path
val path = expr.via :+ expr.peerId
val returnPath = path.reverse
val returnLeaf = {
FuncOp.leaf(
OnTag(
ValuesAlgebra.valueToModel(returnPath.last),
returnPath.map(ValuesAlgebra.valueToModel)
)
)
}
FuncOp.node(
OnTag(
ValuesAlgebra.valueToModel(expr.peerId),
path.map(ValuesAlgebra.valueToModel)
),
Chain(
op,
returnLeaf
)
)
case m => Model.error("On body is not an op, it's " + m)
})
)

View File

@ -1,5 +1,6 @@
package aqua
import aqua.model.Call
import aqua.parser.expr.{
AbilityIdExpr,
AliasExpr,
@ -37,17 +38,23 @@ import aqua.semantics.LiteralType.{bool, number, string}
import aqua.semantics.{LiteralType, ScalarType}
import org.scalatest.EitherValues
import scala.collection.mutable
import scala.language.implicitConversions
object Utils {
implicit def toAb(str: String): Ability[Id] = Ability[Id](str)
implicit def toName(str: String): Name[Id] = Name[Id](str)
implicit def toNameOp(str: Option[String]): Option[Name[Id]] = str.map(s => toName(s))
implicit def toFields(fields: List[String]): List[IntoField[Id]] = fields.map(f => IntoField[Id](f))
implicit def toFields(fields: List[String]): List[IntoField[Id]] =
fields.map(f => IntoField[Id](f))
implicit def toVar(name: String): VarLambda[Id] = VarLambda[Id](toName(name), Nil)
implicit def toVarOp(name: Option[String]): Option[VarLambda[Id]] =
name.map(s => VarLambda[Id](toName(s), Nil))
implicit def toVarLambda(name: String, fields: List[String]): VarLambda[Id] =
VarLambda[Id](toName(name), toFields(fields))
implicit def toLiteral(name: String, t: LiteralType): Literal[Id] = Literal[Id](name, t)
@ -58,18 +65,30 @@ object Utils {
implicit def toCustomType(str: String): CustomTypeToken[Id] = CustomTypeToken[Id](str)
def toArrayType(str: String): ArrayTypeToken[Id] = ArrayTypeToken[Id]((), str)
implicit def toArrowType(args: List[DataTypeToken[Id]], res: Option[DataTypeToken[Id]]): ArrowTypeToken[Id] =
implicit def toArrowType(
args: List[DataTypeToken[Id]],
res: Option[DataTypeToken[Id]]
): ArrowTypeToken[Id] =
ArrowTypeToken[Id]((), args, res)
implicit def toCustomArg(str: String, customType: String): Arg[Id] = Arg[Id](str, toCustomType(customType))
implicit def toCustomArg(str: String, customType: String): Arg[Id] =
Arg[Id](str, toCustomType(customType))
implicit def toArg(str: String, typeToken: TypeToken[Id]): Arg[Id] = Arg[Id](str, typeToken)
implicit def toArgSc(str: String, scalarType: ScalarType): Arg[Id] =
Arg[Id](str, scToBt(scalarType))
implicit def scToBt(sc: ScalarType): BasicTypeToken[Id] = BasicTypeToken[Id](sc)
val boolSc: BasicTypeToken[Id] = BasicTypeToken[Id](ScalarType.bool)
val stringSc: BasicTypeToken[Id] = BasicTypeToken[Id](ScalarType.string)
}
trait Utils extends EitherValues {
val emptyCall = Call(Nil, None)
def parseExpr(str: String): CallArrowExpr[Id] =
CallArrowExpr.p[Id].parseAll(str).value
@ -110,4 +129,8 @@ trait Utils extends EitherValues {
ArrowTypeExpr.p[Id].parseAll(str).value
def funcExpr(str: String): FuncExpr[Id] = FuncExpr.p[Id].parseAll(str).value
implicit class QueueHelper[T](q: mutable.Queue[T]) {
def d(): T = q.dequeue()
}
}

View File

@ -1,19 +1,44 @@
package aqua.parser
import aqua.Utils
import aqua.parser.Ast.Tree
import aqua.parser.expr.FuncExpr
import aqua.parser.lexer.{ArrowTypeToken, BasicTypeToken}
import aqua.model.{
Call,
CallArrowTag,
CallServiceTag,
FuncCallable,
LiteralModel,
OnTag,
OpTag,
ScriptModel,
SeqTag,
XorTag
}
import aqua.{SyntaxError, Utils}
import aqua.parser.Ast.{parser, Tree}
import aqua.parser.expr.{
AbilityIdExpr,
ArrowTypeExpr,
CallArrowExpr,
FuncExpr,
IfExpr,
OnExpr,
ReturnExpr,
RootExpr,
ServiceExpr
}
import aqua.parser.lexer.{ArrowTypeToken, BasicTypeToken, EqOp}
import aqua.semantics.ScalarType.{bool, u64}
import cats.{Eval, Id}
import cats.syntax.traverse._
import cats.syntax.foldable._
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import aqua.parser.lift.LiftParser.Implicits.idLiftParser
import aqua.semantics.Semantics
import aqua.semantics.Semantics.{folder, Alg}
import cats.data.Chain
import cats.data.{Chain, NonEmptyChain, Validated}
import cats.free.Cofree
import scala.collection.mutable
import scala.language.implicitConversions
class FuncExprSpec extends AnyFlatSpec with Matchers with Utils {
@ -21,18 +46,35 @@ class FuncExprSpec extends AnyFlatSpec with Matchers with Utils {
import aqua.semantics.ScalarType.{string, u32}
"func header" should "parse" in {
funcExpr("func some() -> bool") should be(FuncExpr("some", List(), Some(bool: BasicTypeToken[Id]), None))
funcExpr("func some() -> bool") should be(
FuncExpr("some", List(), Some(bool: BasicTypeToken[Id]), None)
)
funcExpr("func some()") should be(FuncExpr("some", List(), None, None))
val arrowToken = ArrowTypeToken[Id]((), List(BasicTypeToken[Id](u32)), Some(BasicTypeToken[Id](bool)))
val arrowToken =
ArrowTypeToken[Id]((), List(BasicTypeToken[Id](u32)), Some(BasicTypeToken[Id](bool)))
funcExpr("func some(peer: PeerId, other: u32 -> bool)") should be(
FuncExpr(toName("some"), List(toCustomArg("peer", "PeerId"), toArg("other", arrowToken)), None, None)
FuncExpr(
toName("some"),
List(toCustomArg("peer", "PeerId"), toArg("other", arrowToken)),
None,
None
)
)
val arrowToken2 =
ArrowTypeToken[Id]((), List(BasicTypeToken[Id](u32), BasicTypeToken[Id](u64)), Some(BasicTypeToken[Id](bool)))
ArrowTypeToken[Id](
(),
List(BasicTypeToken[Id](u32), BasicTypeToken[Id](u64)),
Some(BasicTypeToken[Id](bool))
)
funcExpr("func some(peer: PeerId, other: u32, u64 -> bool)") should be(
FuncExpr(toName("some"), List(toCustomArg("peer", "PeerId"), toArg("other", arrowToken2)), None, None)
FuncExpr(
toName("some"),
List(toCustomArg("peer", "PeerId"), toArg("other", arrowToken2)),
None,
None
)
)
val arrowToken3 = ArrowTypeToken[Id]((), List(BasicTypeToken[Id](u32)), None)
@ -44,25 +86,26 @@ class FuncExprSpec extends AnyFlatSpec with Matchers with Utils {
None
)
)
}
"on" should "parse on x: y" in {
val script =
"""func a():
| on peer.id:
| x <- Ab.func()
| Peer "some id"
| call(true)""".stripMargin
val c: Cofree[Chain, Expr[Id]] = FuncExpr.ast[Id](Indent()).parseAll(script).value
val a = Ast(c).cata(folder[Id, Alg[Id, *]]).value
// a.run
println(a)
FuncExpr.ast[Id](Indent()).parseAll(script).isRight should be(true)
def checkHeadGetTail(
tree: Cofree[Chain, Expr[Id]],
headCheck: Expr[Id],
lengthCheck: Int
): Chain[Cofree[Chain, Expr[Id]]] = {
tree.head should be(headCheck)
val tail = tree.tailForced
tail.length should be(lengthCheck)
tail
}
"if" should "parse if x == y" in {
def headTail[T](
tree: Cofree[Chain, T]
): (T, List[Cofree[Chain, T]]) = {
(tree.head, tree.tailForced.toList)
}
"func expr" should "parse on x: y" in {
val script =
"""func a():
| if peer.id == other:
@ -70,59 +113,153 @@ class FuncExprSpec extends AnyFlatSpec with Matchers with Utils {
| Peer "some id"
| call(true)""".stripMargin
FuncExpr.ast[Id](Indent()).parseAll(script).isRight should be(true)
val tree = FuncExpr.ast[Id](Indent()).parseAll(script).value
val funcBody = checkHeadGetTail(tree, FuncExpr("a", Nil, None, None), 1).toList
println("body: " + funcBody)
val ifBody =
checkHeadGetTail(
funcBody.head,
IfExpr(toVarLambda("peer", List("id")), EqOp[Id](true), toVar("other")),
3
).toList
ifBody.head.head should be(CallArrowExpr(Some(toName("x")), Some(toAb("Ab")), "func", Nil))
ifBody(1).head should be(AbilityIdExpr(toAb("Peer"), toStr("some id")))
ifBody(2).head should be(CallArrowExpr(None, None, "call", List(toBool(true))))
}
/*
"function" should "parse getTime as a whole" in {
val func =
"""func getTime(peer: PeerId, ret: u32 -> ()) -> string:
| on peer:
| Peer "peer"
| t <- Peer.timestamp()
| ret(t)""".stripMargin
"some" should "other" in {
val script =
"""service Local("local"):
| gt: -> bool
|
|func tryGen() -> bool:
| on "deeper" via "deep":
| v <- Local.gt()
| <- v
|
|func genC(val: string) -> bool:
| one <- Local.gt()
| on "smth" via "else":
| two <- tryGen()
| three <- Local.gt()
| <- two""".stripMargin
DefFunc.`deffunc`.parseAll(func).right.value should be(
DefFunc[Id, HNil](
getTimeHead,
NonEmptyList.of(
On[Id, HNil](
VarLambda[Id]("peer", Nil),
NonEmptyList.of(
AbilityId[Id, HNil]("Peer", Literal[Id]("\"peer\"", LiteralType.string), HNil),
Extract[Id, HNil]("t", AbilityFuncCall[Id, HNil]("Peer", "timestamp", "Peer.timestamp", Nil, HNil), HNil)
),
HNil
),
FuncCall[Id, HNil]("ret", VarLambda[Id]("t", Nil) :: Nil, HNil)
),
HNil
)
)
val tree = parser[Id](Indent()).parseAll(script).value
val qTree = tree.tree.foldLeft(mutable.Queue.empty[Expr[Id]]) { case (acc, tag) =>
acc.enqueue(tag)
}
"function" should "parse getTime with no return" in {
val func =
"""func getTime(peer: PeerId, ret: u32 -> ()) -> string:
| on peer:
| Peer "peer"
| t <- Peer.timestamp()""".stripMargin
qTree.d() shouldBe RootExpr()
// Local service
qTree.d() shouldBe ServiceExpr(toAb("Local"), Some(toStr("local")))
qTree.d() shouldBe ArrowTypeExpr("gt", toArrowType(Nil, Some(scToBt(bool))))
qTree.d() shouldBe FuncExpr("tryGen", Nil, Some(scToBt(bool)), Some("v"))
qTree.d() shouldBe OnExpr(toStr("deeper"), List(toStr("deep")))
qTree.d() shouldBe CallArrowExpr(Some("v"), Some(toAb("Local")), "gt", Nil)
qTree.d() shouldBe ReturnExpr(toVar("v"))
// genC function
qTree.d() shouldBe FuncExpr("genC", List(toArgSc("val", string)), Some(boolSc), Some("two"))
qTree.d() shouldBe CallArrowExpr(Some("one"), Some(toAb("Local")), "gt", List())
qTree.d() shouldBe OnExpr(toStr("smth"), List(toStr("else")))
qTree.d() shouldBe CallArrowExpr(Some("two"), None, "tryGen", List())
qTree.d() shouldBe CallArrowExpr(Some("three"), Some(toAb("Local")), "gt", List())
qTree.d() shouldBe ReturnExpr(toVar("two"))
DefFunc.`deffunc`.parseAll(func).right.value should be(
DefFunc[Id, HNil](
getTimeHead,
NonEmptyList.of(
On[Id, HNil](
VarLambda[Id]("peer", Nil),
NonEmptyList.of(
AbilityId[Id, HNil]("Peer", Literal[Id]("\"peer\"", LiteralType.string), HNil),
Extract[Id, HNil]("t", AbilityFuncCall[Id, HNil]("Peer", "timestamp", "Peer.timestamp", Nil, HNil), HNil)
),
HNil
)
),
HNil
)
)
}*/
val f =
Semantics.generateModel(tree).toList.head.asInstanceOf[ScriptModel]
val funcs = f.funcs.toList
val funcTryGen = funcs.head
val funcOpTryGen = funcTryGen.body.resolveTopology()
val qTryGen = funcOpTryGen.tree.foldLeft(mutable.Queue.empty[OpTag]) { case (acc, tag) =>
acc.enqueue(tag)
}
val smth = LiteralModel("\"smth\"")
val smthOn = OnTag(smth, Nil)
val smthCall = CallServiceTag(
LiteralModel("\"op\""),
"identity",
emptyCall,
Some(LiteralModel("\"smth\""))
)
val elseL = LiteralModel("\"else\"")
val elseOn = OnTag(elseL, Nil)
val elseCall = CallServiceTag(
LiteralModel("\"op\""),
"identity",
emptyCall,
Some(LiteralModel("\"else\""))
)
val deeper = LiteralModel("\"deeper\"")
val deeperOn = OnTag(deeper, Nil)
val deeperCall = CallServiceTag(
LiteralModel("\"op\""),
"identity",
emptyCall,
Some(LiteralModel("\"deeper\""))
)
val deep = LiteralModel("\"deep\"")
val deepOn = OnTag(deep, Nil)
val deepCall = CallServiceTag(
LiteralModel("\"op\""),
"identity",
emptyCall,
Some(LiteralModel("\"deep\""))
)
val local = LiteralModel("\"local\"")
// tag that we will go to 'deeper'
qTryGen.d() shouldBe deeperOn
// move to 'deep' node
qTryGen.d() shouldBe deepOn
qTryGen.d() shouldBe deepCall
// move to 'deeper' node
qTryGen.d() shouldBe deeperOn
qTryGen.d() shouldBe deeperCall
// a call must be with `on` too, so we need to call 'deeper' after we will go out of the scope
qTryGen.d() shouldBe CallServiceTag(local, "gt", Call(Nil, Some("v")), Some(deeper))
qTryGen.d() shouldBe deepOn
// return to 'deeper' node
qTryGen.d() shouldBe deeperOn
qTryGen.d() shouldBe deeperCall
// return to 'deep' node
qTryGen.d() shouldBe deepOn
qTryGen.d() shouldBe deepCall
val funcGenComplex = f.funcs.toList(1)
val funcOpGenComplex = funcGenComplex.body.resolveTopology()
val qGenComplex = funcOpGenComplex.tree.foldLeft(mutable.Queue.empty[OpTag]) {
case (acc, tag) =>
acc.enqueue(tag)
}
qGenComplex.d() shouldBe SeqTag
qGenComplex.d() shouldBe CallServiceTag(local, "gt", Call(List(), Some("one")), None)
// tag that we will go to 'smth'
qGenComplex.d() shouldBe smthOn
// move to 'else' node
qGenComplex.d() shouldBe elseOn
qGenComplex.d() shouldBe elseCall
// move to 'smth' node
qGenComplex.d() shouldBe smthOn
qGenComplex.d() shouldBe smthCall
qGenComplex.d() shouldBe CallArrowTag(None, "tryGen", Call(List(), Some("two")))
qGenComplex.d() shouldBe elseOn
// return to 'smth' node
qGenComplex.d() shouldBe smthOn
qGenComplex.d() shouldBe smthCall
// return to 'else' node
qGenComplex.d() shouldBe elseOn
qGenComplex.d() shouldBe elseCall
qGenComplex.d() shouldBe CallServiceTag(local, "gt", Call(List(), Some("three")), None)
}
}