From abd101c4f9ded2d85ccc73736d87ccf72181cd92 Mon Sep 17 00:00:00 2001 From: Dmitry Kurinskiy Date: Tue, 12 Apr 2022 16:27:04 +0300 Subject: [PATCH] Fix for greedy par (#487) --- aqua-src/hack.aqua | 30 +++++++++++-------- cli/.jvm/src/main/scala/aqua/Test.scala | 4 +-- .../src/main/scala/aqua/raw/ops/RawTag.scala | 22 +++++++++----- .../scala/aqua/raw/ops/RawTagGivens.scala | 25 ++++++++++------ .../scala/aqua/parser/expr/func/ParExpr.scala | 4 ++- .../src/main/scala/aqua/semantics/Prog.scala | 13 ++++++-- .../main/scala/aqua/semantics/Semantics.scala | 7 +++-- .../aqua/semantics/expr/func/ForSem.scala | 5 ++-- .../scala/aqua/semantics/SemanticsSpec.scala | 16 +++++----- 9 files changed, 82 insertions(+), 44 deletions(-) diff --git a/aqua-src/hack.aqua b/aqua-src/hack.aqua index 4a12ffa2..c0b2ac2c 100644 --- a/aqua-src/hack.aqua +++ b/aqua-src/hack.aqua @@ -1,12 +1,18 @@ -func emptySugar() -> []u32, []string, []string, *string, ?u32, []u32, ?string: - numOp = ?[] - strArr = [] - strStream = *[] - strEmptyStream: *string - for i <- ?[]: - strEmptyStream <<- "some" - for i <- *[]: - strEmptyStream <<- "some" - for i <- []: - strEmptyStream <<- "some" - <- numOp, strArr, strStream, strEmptyStream, [], ?[], *[] +service Peer("peer"): + hodes: -> []string + timeout: i32, string -> string + +func test_timeout() -> string: + on HOST_PEER_ID: + nodes <- Peer.hodes() + results: *string + + for node <- nodes par: + on node: + results <<- node + + timeout: *string + join results[999] + par join results[123] + + <- timeout! \ No newline at end of file diff --git a/cli/.jvm/src/main/scala/aqua/Test.scala b/cli/.jvm/src/main/scala/aqua/Test.scala index ae1d923b..faed71c9 100644 --- a/cli/.jvm/src/main/scala/aqua/Test.scala +++ b/cli/.jvm/src/main/scala/aqua/Test.scala @@ -22,11 +22,11 @@ object Test extends IOApp.Simple { start <- IO(System.currentTimeMillis()) _ <- AquaPathCompiler .compileFilesTo[IO]( - Path("./aqua-src/call_arrow.aqua"), + Path("./aqua-src/hack.aqua"), List(Path("./aqua")), Option(Path("./target")), TypeScriptBackend, - TransformConfig(wrapWithXor = true) + TransformConfig(wrapWithXor = false) ) .map { case Validated.Invalid(errs) => diff --git a/model/raw/src/main/scala/aqua/raw/ops/RawTag.scala b/model/raw/src/main/scala/aqua/raw/ops/RawTag.scala index 6318bc8f..f7f9abdb 100644 --- a/model/raw/src/main/scala/aqua/raw/ops/RawTag.scala +++ b/model/raw/src/main/scala/aqua/raw/ops/RawTag.scala @@ -1,16 +1,14 @@ package aqua.raw.ops -import aqua.raw.arrow.FuncRaw -import aqua.raw.value.ValueRaw -import aqua.raw.value.CallArrowRaw -import cats.data.{Chain, NonEmptyList} -import cats.free.Cofree -import cats.Show -import cats.Eval import aqua.raw.Raw +import aqua.raw.arrow.FuncRaw import aqua.raw.ops.RawTag.Tree +import aqua.raw.value.{CallArrowRaw, ValueRaw} import aqua.tree.{TreeNode, TreeNodeCompanion} import aqua.types.{ArrowType, ProductType} +import cats.{Eval, Show} +import cats.data.{Chain, NonEmptyList} +import cats.free.Cofree sealed trait RawTag extends TreeNode[RawTag] { @@ -46,6 +44,16 @@ sealed trait SeqGroupTag extends GroupTag object SeqGroupTag extends SeqGroupTag { override def toString: String = "SeqGroup" + + def ungroupSingle(tree: Tree): Tree = tree.head match { + case SeqGroupTag => + val children = tree.tail.value + children.headOption.fold(tree) { + case h if children.length == 1 => h + case _ => tree + } + case _ => tree + } } sealed trait ParGroupTag extends GroupTag diff --git a/model/raw/src/main/scala/aqua/raw/ops/RawTagGivens.scala b/model/raw/src/main/scala/aqua/raw/ops/RawTagGivens.scala index 9844883d..c5328fed 100644 --- a/model/raw/src/main/scala/aqua/raw/ops/RawTagGivens.scala +++ b/model/raw/src/main/scala/aqua/raw/ops/RawTagGivens.scala @@ -30,15 +30,19 @@ trait RawTagGivens { given Semigroup[RawTag.Tree] with - override def combine(x: RawTag.Tree, y: RawTag.Tree): RawTag.Tree = - (x.head, y.head) match { - case (_, XorParTag(xor, par)) => combine(combine(x, xor), par) - case (XorParTag(xor, par), _) => combine(combine(xor, par), y) - case (SeqTag, SeqTag) => y.copy(tail = (x.tail, y.tail).mapN(_ ++ _)) - case (_, SeqTag) => y.copy(tail = y.tail.map(_.prepend(x))) - case (SeqTag, _) => x.copy(tail = x.tail.map(_.append(y))) - case _ => SeqTag.wrap(x, y) + override def combine(x: RawTag.Tree, y: RawTag.Tree): RawTag.Tree = { + // Remove right-asscoc protection of Seq with single child + val flatX = SeqGroupTag.ungroupSingle(x) + val flatY = SeqGroupTag.ungroupSingle(y) + (flatX.head, flatY.head) match { + case (_, XorParTag(xor, par)) => combine(combine(flatX, xor), par) + case (XorParTag(xor, par), _) => combine(combine(xor, par), flatY) + case (SeqTag, SeqTag) => flatY.copy(tail = (flatX.tail, flatY.tail).mapN(_ ++ _)) + case (_, SeqTag) => flatY.copy(tail = flatY.tail.map(_.prepend(flatX))) + case (SeqTag, _) => flatX.copy(tail = flatX.tail.map(_.append(flatY))) + case _ => SeqTag.wrap(flatX, flatY) } + } // Semigroup for foldRight processing def rightAssocCombine(x: RawTag.Tree, y: RawTag.Tree): RawTag.Tree = @@ -51,7 +55,10 @@ trait RawTagGivens { SeqGroupTag.wrap(y.copy(tail = (x.tail, y.tail).mapN(_ ++ _))) case (XorTag, ParTag) => XorParTag(x, y).leaf case (_, ParTag | XorTag) => - SeqTag.wrap(y.copy(tail = y.tail.map(_.prepend(x)))) + // When right-associative tag is combined with left-associative, + // we need result to be left-associative to prevent greedy behavior. + // SeqGroupTag does just this. + SeqGroupTag.wrap(y.copy(tail = y.tail.map(_.prepend(x)))) case (_, XorParTag(xor, par)) => rightAssocCombine(rightAssocCombine(x, xor), par) case _ => x |+| y diff --git a/parser/src/main/scala/aqua/parser/expr/func/ParExpr.scala b/parser/src/main/scala/aqua/parser/expr/func/ParExpr.scala index 33aaa378..a9dafb3a 100644 --- a/parser/src/main/scala/aqua/parser/expr/func/ParExpr.scala +++ b/parser/src/main/scala/aqua/parser/expr/func/ParExpr.scala @@ -18,7 +18,9 @@ case class ParExpr[F[_]](point: Token[F]) extends Expr[F](ParExpr, point) { } object ParExpr extends Expr.Prefix() { - override def continueWith: List[Expr.Lexem] = CallArrowExpr :: OnExpr :: ForExpr :: Nil + + override def continueWith: List[Expr.Lexem] = + CallArrowExpr :: OnExpr :: ForExpr :: JoinExpr :: Nil override val p: Parser[Expr[Span.S]] = `par`.lift.map(Token.lift[Span.S, Unit](_)).map(ParExpr(_)) diff --git a/semantics/src/main/scala/aqua/semantics/Prog.scala b/semantics/src/main/scala/aqua/semantics/Prog.scala index 70c7c0f8..2de2a305 100644 --- a/semantics/src/main/scala/aqua/semantics/Prog.scala +++ b/semantics/src/main/scala/aqua/semantics/Prog.scala @@ -2,9 +2,10 @@ package aqua.semantics import aqua.parser.lexer.Token import aqua.semantics.rules.abilities.AbilitiesAlgebra +import aqua.semantics.rules.names.NamesAlgebra import cats.Monad -import cats.syntax.flatMap._ -import cats.syntax.functor._ +import cats.syntax.flatMap.* +import cats.syntax.functor.* import scala.language.implicitConversions @@ -28,6 +29,14 @@ sealed abstract class Prog[Alg[_]: Monad, A] extends (Alg[A] => Alg[A]) { (_: Unit, m: A) => Ab.endScope() as m ) ) + + def namesScope[S[_]](token: Token[S])(implicit N: NamesAlgebra[S, Alg]): Prog[Alg, A] = + wrap( + RunAround( + N.beginScope(token), + (_: Unit, m: A) => N.endScope() as m + ) + ) } case class RunAfter[Alg[_]: Monad, A](prog: Alg[A]) extends Prog[Alg, A] { diff --git a/semantics/src/main/scala/aqua/semantics/Semantics.scala b/semantics/src/main/scala/aqua/semantics/Semantics.scala index b20a9a6d..183b294f 100644 --- a/semantics/src/main/scala/aqua/semantics/Semantics.scala +++ b/semantics/src/main/scala/aqua/semantics/Semantics.scala @@ -1,6 +1,6 @@ package aqua.semantics -import aqua.raw.ops.FuncOp +import aqua.raw.ops.{FuncOp, SeqGroupTag} import aqua.raw.{Raw, RawContext, RawPart} import aqua.parser.lexer.Token import aqua.parser.{Ast, Expr} @@ -42,7 +42,10 @@ object Semantics extends Logging { case (prev, acc) => prev :: acc } } - .map(_.reduceLeftOption(_ |+| _).getOrElse(Raw.empty("AST is empty"))) + .map( + _.reduceLeftOption(_ |+| _) + .getOrElse(Raw.empty("AST is empty")) + ) ) } diff --git a/semantics/src/main/scala/aqua/semantics/expr/func/ForSem.scala b/semantics/src/main/scala/aqua/semantics/expr/func/ForSem.scala index 8cc385f0..06bf0681 100644 --- a/semantics/src/main/scala/aqua/semantics/expr/func/ForSem.scala +++ b/semantics/src/main/scala/aqua/semantics/expr/func/ForSem.scala @@ -27,7 +27,7 @@ class ForSem[S[_]](val expr: ForExpr[S]) extends AnyVal { ): Prog[F, Raw] = Prog .around( - N.beginScope(expr.item) >> V.valueToRaw(expr.iterable).flatMap[Option[ValueRaw]] { + V.valueToRaw(expr.iterable).flatMap[Option[ValueRaw]] { case Some(vm) => vm.`type` match { case t: BoxType => @@ -71,7 +71,8 @@ class ForSem[S[_]](val expr: ForExpr[S]) extends AnyVal { case _ => Raw.error("Wrong body of the For expression") } - ) <* N.endScope() + ) ) + .namesScope[S](expr.token) .abilitiesScope[S](expr.token) } diff --git a/semantics/src/test/scala/aqua/semantics/SemanticsSpec.scala b/semantics/src/test/scala/aqua/semantics/SemanticsSpec.scala index 4b0c07de..187ebeb3 100644 --- a/semantics/src/test/scala/aqua/semantics/SemanticsSpec.scala +++ b/semantics/src/test/scala/aqua/semantics/SemanticsSpec.scala @@ -46,14 +46,16 @@ class SemanticsSpec extends AnyFlatSpec with Matchers { CallArrowRawTag.service(LiteralRaw.quote("srv1"), "fn1", emptyCall, "A", arrowType).leaf val expected = - ParTag.wrap( - OnTag( - LiteralRaw("\"other-peer\"", LiteralType.string), - Chain.empty - ).wrap( + SeqGroupTag.wrap( + ParTag.wrap( + OnTag( + LiteralRaw("\"other-peer\"", LiteralType.string), + Chain.empty + ).wrap( + serviceCall + ), serviceCall - ), - serviceCall + ) ) proc.equalsOrShowDiff(expected) should be(true)