From abcb63db3b1acbe68dd49539016b807071b24a4c Mon Sep 17 00:00:00 2001 From: Dima Date: Mon, 22 Jan 2024 13:08:06 +0300 Subject: [PATCH] perf(compiler): Inliner optimization [LNG-322] (#1047) --- .../.jvm/src/main/scala/aqua/api/Test.scala | 7 +- aqua-src/antithesis.aqua | 55 +++++------ .../src/main/scala/aqua/backend/air/Air.scala | 97 +++++++++++++------ build.sbt | 15 ++- .../scala/aqua/compiler/AquaCompiler.scala | 17 +--- .../scala/aqua/compiler/CompilerAPI.scala | 25 ++--- .../model/inline/state/InliningState.scala | 5 +- .../aqua/model/inline/state/Mangler.scala | 53 +++------- .../scala/aqua/model/inline/ManglerSpec.scala | 80 +++++++++++++++ .../model/inline/RawValueInlinerSpec.scala | 39 ++++---- .../src/main/scala/aqua/raw/RawContext.scala | 1 - .../scala/aqua/parser/lift/FileSpan.scala | 3 +- .../scala/aqua/semantics/CompilerState.scala | 2 +- .../main/scala/aqua/semantics/Semantics.scala | 3 +- .../semantics/expr/func/ServiceIdSem.scala | 4 +- .../rules/locations/LocationsState.scala | 5 +- .../rules/mangler/ManglerInterpreter.scala | 20 +--- .../rules/mangler/ManglerState.scala | 24 ----- .../scala/aqua/logging/LogFormatter.scala | 4 +- .../scala/aqua/mangler/ManglerState.scala | 65 +++++++++++++ 20 files changed, 313 insertions(+), 211 deletions(-) create mode 100644 model/inline/src/test/scala/aqua/model/inline/ManglerSpec.scala delete mode 100644 semantics/src/main/scala/aqua/semantics/rules/mangler/ManglerState.scala create mode 100644 utils/mangler/src/main/scala/aqua/mangler/ManglerState.scala diff --git a/api/api/.jvm/src/main/scala/aqua/api/Test.scala b/api/api/.jvm/src/main/scala/aqua/api/Test.scala index f587abaa..d81c0041 100644 --- a/api/api/.jvm/src/main/scala/aqua/api/Test.scala +++ b/api/api/.jvm/src/main/scala/aqua/api/Test.scala @@ -6,7 +6,6 @@ import aqua.compiler.AquaCompiled import aqua.files.FileModuleId import cats.data.Chain -import cats.data.Validated.{Invalid, Valid} import cats.effect.{IO, IOApp} import fs2.io.file.{Files, Path} import fs2.{Stream, text} @@ -14,14 +13,16 @@ import fs2.{Stream, text} object Test extends IOApp.Simple { override def run: IO[Unit] = { + APICompilation .compilePath( "./aqua-src/antithesis.aqua", Imports.fromMap(Map("/" -> Map("" -> List("./aqua")))), AquaAPIConfig(targetType = TypeScriptType), TypeScriptBackend(false, "IFluenceClient$$") - ) - .flatMap { res => + ).timed + .flatMap { case (duration, res) => + println("Compilation time: " + duration.toMillis) val (warnings, result) = res.value.run IO.delay { diff --git a/aqua-src/antithesis.aqua b/aqua-src/antithesis.aqua index 37dd102b..446f57b9 100644 --- a/aqua-src/antithesis.aqua +++ b/aqua-src/antithesis.aqua @@ -1,40 +1,33 @@ aqua M -export bugLng317 +export returnSrvAsAbility -service MyOp("op"): - identity(s: string) -> string +ability MyAb: + call() -> string -ability WorkerJob: - runOnSingleWorker(w: string) -> []string +service MySrv("default-id"): + call() -> string -func runJob(j: -> []string) -> []string: - <- j() +func mySrvDefault() -> MyAb: + <- MySrv -func disjoint_run{WorkerJob}() -> -> []string: - run = func () -> []string: - r <- WorkerJob.runOnSingleWorker("a") - <- r - <- run +func mySrvResolved() -> MyAb: + MySrv "resolved-id" + <- MySrv -func empty() -> string: - a = "empty" - <- a +func mySrvThird() -> MyAb: + MySrv "third-id" + <- MySrv -func bugLng317() -> []string: +func useMyAb{MyAb}() -> string: + <- MyAb.call() - res: *string - - outer = () -> string: - <- empty() - - clos = () -> -> []string: - job2 = () -> []string: - res <- outer() - res <- MyOp.identity("identity") - <- res - <- job2 - worker_job = WorkerJob(runOnSingleWorker = clos()) - subnet_job <- disjoint_run{worker_job}() - finalRes <- runJob(subnet_job) - <- finalRes +func returnSrvAsAbility() -> []string: + result: *string + MySrvDefault <- mySrvDefault() + MySrvResolved <- mySrvResolved() + MySrvThird <- mySrvThird() + result <- useMyAb{MySrvDefault}() + result <- useMyAb{MySrvResolved}() + result <- useMyAb{MySrvThird}() + <- result \ No newline at end of file diff --git a/backend/air/src/main/scala/aqua/backend/air/Air.scala b/backend/air/src/main/scala/aqua/backend/air/Air.scala index 56a7bf22..b619b29a 100644 --- a/backend/air/src/main/scala/aqua/backend/air/Air.scala +++ b/backend/air/src/main/scala/aqua/backend/air/Air.scala @@ -120,42 +120,77 @@ object Air { case class Comment(comment: String, air: Air) extends Air(Keyword.NA) - private def show(depth: Int, air: Air): String = { - def showNext(a: Air) = show(depth + 1, a) + private def showInternal(space: String, sb: StringBuilder, air: Air): Unit = { - val space = " " * depth + def showNext(a: Air): Unit = showInternal(space + " ", sb, a) air match { case Air.Comment(c, a) => - space + "; " + c.replace("\n", "\n" + space + "; ") + "\n" + - show(depth, a) - case _ => - s"$space(${air.keyword.value}" + - (air match { - case Air.Null ⇒ "" - case Air.Never ⇒ "" - case Air.Next(label) ⇒ s" $label" - case Air.New(item, inst) ⇒ s" ${item.show}\n${showNext(inst)}$space" - case Air.Fold(iter, label, inst, lastInst) ⇒ - val l = show(depth + 1, lastInst) - s" ${iter.show} $label\n${showNext(inst)}$l$space" - case Air.Match(left, right, inst) ⇒ - s" ${left.show} ${right.show}\n${showNext(inst)}$space" - case Air.Mismatch(left, right, inst) ⇒ - s" ${left.show} ${right.show}\n${showNext(inst)}$space" - case Air.Par(l, r) ⇒ s"\n${showNext(l)}${showNext(r)}$space" - case Air.Seq(l, r) ⇒ s"\n${showNext(l)}${showNext(r)}$space" - case Air.Xor(l, r) ⇒ s"\n${showNext(l)}${showNext(r)}$space" - case Air.Call(triplet, args, res) ⇒ - s" ${triplet.show} [${args.map(_.show).mkString(" ")}]${res.fold("")(" " + _)}" - case Air.Ap(operand, result) ⇒ s" ${operand.show} $result" - case Air.ApStreamMap(key, operand, result) ⇒ s" (${key.show} ${operand.show}) $result" - case Air.Fail(operand) => s" ${operand.show}" - case Air.Canon(operand, peerId, result) ⇒ s" ${peerId.show} ${operand.show} $result" - case Air.Comment(_, _) => ";; Should not be displayed" - }) + ")\n" - } + sb.append(space) + .append("; ") + .append(c.replace("\n", "\n" + space + "; ")) + .append("\n") + showInternal(space, sb, a) + + case _ => + sb.append(s"$space(${air.keyword.value}") + (air match { + case Air.Null ⇒ + case Air.Never ⇒ + case Air.Next(label) ⇒ sb.append(s" $label") + case Air.New(item, inst) ⇒ + sb.append(s" ${item.show}\n") + showNext(inst) + sb.append(space) + case Air.Fold(iter, label, inst, lastInst) ⇒ + sb.append(" ").append(s" ${iter.show} $label\n") + showNext(inst) + showNext(lastInst) + sb.append(space) + case Air.Match(left, right, inst) ⇒ + sb.append(s" ${left.show} ${right.show}\n") + showNext(inst) + sb.append(space) + case Air.Mismatch(left, right, inst) ⇒ + sb.append(s" ${left.show} ${right.show}\n") + showNext(inst) + sb.append(space) + case Air.Par(l, r) ⇒ + sb.append("\n") + showNext(l) + showNext(r) + sb.append(space) + case Air.Seq(l, r) ⇒ + sb.append("\n") + showNext(l) + showNext(r) + sb.append(space) + case Air.Xor(l, r) ⇒ + sb.append("\n") + showNext(l) + showNext(r) + sb.append(space) + case Air.Call(triplet, args, res) ⇒ + sb.append(s" ${triplet.show} [${args.map(_.show).mkString(" ")}]${res.fold("")(" " + _)}") + case Air.Ap(operand, result) ⇒ + sb.append(s" ${operand.show} $result") + case Air.ApStreamMap(key, operand, result) ⇒ + sb.append(s" (${key.show} ${operand.show}) $result") + case Air.Fail(operand) => sb.append(s" ${operand.show}") + case Air.Canon(operand, peerId, result) ⇒ + sb.append(s" ${peerId.show} ${operand.show} $result") + case Air.Comment(_, _) => ";; Should not be displayed" + }) + sb.append(")\n") + } + } + + private def show(depth: Int, air: Air): String = { + val sb = StringBuilder() + val space = " " * depth + showInternal(space, sb, air) + sb.result() } implicit val s: Show[Air] = Show.show(show(0, _)) diff --git a/build.sbt b/build.sbt index b6e07cb0..61167dba 100644 --- a/build.sbt +++ b/build.sbt @@ -188,7 +188,7 @@ lazy val inline = crossProject(JVMPlatform, JSPlatform) .crossType(CrossType.Pure) .in(file("model/inline")) .settings(commons) - .dependsOn(raw, model) + .dependsOn(raw, model, mangler) lazy val transform = crossProject(JVMPlatform, JSPlatform) .withoutSuffixFor(JVMPlatform) @@ -207,7 +207,7 @@ lazy val semantics = crossProject(JVMPlatform, JSPlatform) "dev.optics" %%% "monocle-macro" % monocleV ) ) - .dependsOn(raw, parser, errors) + .dependsOn(raw, parser, errors, mangler) lazy val compiler = crossProject(JVMPlatform, JSPlatform) .withoutSuffixFor(JVMPlatform) @@ -253,6 +253,17 @@ lazy val logging = crossProject(JVMPlatform, JSPlatform) ) ) +lazy val mangler = crossProject(JVMPlatform, JSPlatform) + .withoutSuffixFor(JVMPlatform) + .crossType(CrossType.Pure) + .in(file("utils/mangler")) + .settings(commons) + .settings( + libraryDependencies ++= Seq( + "org.typelevel" %%% "cats-core" % catsV + ) + ) + lazy val constants = crossProject(JVMPlatform, JSPlatform) .withoutSuffixFor(JVMPlatform) .crossType(CrossType.Pure) diff --git a/compiler/src/main/scala/aqua/compiler/AquaCompiler.scala b/compiler/src/main/scala/aqua/compiler/AquaCompiler.scala index 66b7c744..94643ff8 100644 --- a/compiler/src/main/scala/aqua/compiler/AquaCompiler.scala +++ b/compiler/src/main/scala/aqua/compiler/AquaCompiler.scala @@ -1,30 +1,19 @@ package aqua.compiler -import aqua.backend.Backend import aqua.compiler.AquaError.{ParserError as AquaParserError, *} import aqua.linker.{AquaModule, Linker, Modules} -import aqua.model.AquaContext -import aqua.parser.lift.{LiftParser, Span} import aqua.parser.{Ast, ParserError} -import aqua.raw.RawPart.Parts -import aqua.raw.{RawContext, RawPart} -import aqua.res.AquaRes -import aqua.semantics.header.{HeaderHandler, HeaderSem, Picker} -import aqua.semantics.{CompilerState, Semantics} -import aqua.semantics.{SemanticError, SemanticWarning} +import aqua.semantics.header.{HeaderHandler, Picker} +import aqua.semantics.{SemanticError, Semantics} import cats.arrow.FunctionK import cats.data.* -import cats.data.Validated.{Invalid, Valid, validNec} -import cats.parse.Parser0 import cats.syntax.applicative.* import cats.syntax.either.* import cats.syntax.flatMap.* import cats.syntax.functor.* -import cats.syntax.monoid.* -import cats.syntax.semigroup.* import cats.syntax.traverse.* -import cats.{Comonad, Functor, Monad, Monoid, Order, ~>} +import cats.{Comonad, Monad, Monoid, Order, ~>} import scribe.Logging class AquaCompiler[F[_]: Monad, E, I: Order, S[_]: Comonad, C: Monoid: Picker]( diff --git a/compiler/src/main/scala/aqua/compiler/CompilerAPI.scala b/compiler/src/main/scala/aqua/compiler/CompilerAPI.scala index 722c59a2..c665aafb 100644 --- a/compiler/src/main/scala/aqua/compiler/CompilerAPI.scala +++ b/compiler/src/main/scala/aqua/compiler/CompilerAPI.scala @@ -1,33 +1,22 @@ package aqua.compiler +import aqua.backend.Backend import aqua.compiler.AquaError.* -import aqua.backend.{AirFunction, Backend} -import aqua.linker.{AquaModule, Linker, Modules} import aqua.model.AquaContext -import aqua.parser.lift.{LiftParser, Span} import aqua.parser.{Ast, ParserError} -import aqua.raw.RawPart.Parts -import aqua.raw.{RawContext, RawPart} -import aqua.res.AquaRes +import aqua.raw.RawContext +import aqua.semantics.RawSemantics import aqua.semantics.header.{HeaderHandler, HeaderSem} -import aqua.semantics.{CompilerState, RawSemantics, Semantics} import cats.data.* -import cats.data.Validated.{invalid, validNec, Invalid, Valid} -import cats.parse.Parser0 import cats.syntax.applicative.* -import cats.syntax.flatMap.* -import cats.syntax.foldable.* -import cats.syntax.functor.* -import cats.syntax.monoid.* -import cats.syntax.semigroup.* -import cats.syntax.traverse.* import cats.syntax.either.* -import cats.{~>, Comonad, Monad, Monoid, Order} +import cats.syntax.flatMap.* +import cats.syntax.functor.* +import cats.syntax.traverse.* +import cats.{Comonad, Monad, Monoid, Order} import scribe.Logging -import scala.collection.MapView - object CompilerAPI extends Logging { private def toAquaProcessed[I: Order, E, S[_]: Comonad]( diff --git a/model/inline/src/main/scala/aqua/model/inline/state/InliningState.scala b/model/inline/src/main/scala/aqua/model/inline/state/InliningState.scala index e7c45247..e931cd0a 100644 --- a/model/inline/src/main/scala/aqua/model/inline/state/InliningState.scala +++ b/model/inline/src/main/scala/aqua/model/inline/state/InliningState.scala @@ -1,5 +1,6 @@ package aqua.model.inline.state +import aqua.mangler.ManglerState import aqua.model.{FuncArrow, ValueModel} import aqua.model.inline.state.{Arrows, Counter, Exports, Mangler} import aqua.raw.arrow.FuncRaw @@ -23,7 +24,7 @@ import scribe.Logging * for [[Counter]] */ case class InliningState( - noNames: Set[String] = Set.empty, + noNames: ManglerState = ManglerState(), resolvedExports: Map[String, ValueModel] = Map.empty, resolvedArrows: Map[String, FuncArrow] = Map.empty, instructionCounter: Int = 0 @@ -35,7 +36,7 @@ object InliningState { Counter.Simple.transformS(_.instructionCounter, (acc, i) => acc.copy(instructionCounter = i)) given Mangler[InliningState] = - Mangler.Simple.transformS(_.noNames, (acc, nn) => acc.copy(noNames = nn)) + Mangler[ManglerState].transformS(_.noNames, (acc, nn) => acc.copy(noNames = nn)) given Arrows[InliningState] = Arrows.Simple.transformS(_.resolvedArrows, (acc, aa) => acc.copy(resolvedArrows = aa)) diff --git a/model/inline/src/main/scala/aqua/model/inline/state/Mangler.scala b/model/inline/src/main/scala/aqua/model/inline/state/Mangler.scala index 15a73edb..3e3b4c86 100644 --- a/model/inline/src/main/scala/aqua/model/inline/state/Mangler.scala +++ b/model/inline/src/main/scala/aqua/model/inline/state/Mangler.scala @@ -1,65 +1,36 @@ package aqua.model.inline.state import cats.data.State +import aqua.mangler.ManglerState trait Mangler[S] { self => - def getForbiddenNames: State[S, Set[String]] - - def findNewNames(introduce: Set[String]): State[S, Map[String, String]] - - def findNewName(introduce: String): State[S, String] = - findNewNames(Set(introduce)).map(_.getOrElse(introduce, introduce)) - def findAndForbidName(introduce: String): State[S, String] = - for { - n <- findNewName(introduce) - _ <- forbid(Set(n)) - } yield n + findAndForbidNames(Set(introduce)).map(_.getOrElse(introduce, introduce)) - def findAndForbidNames(introduce: Set[String]): State[S, Map[String, String]] = - for { - n <- findNewNames(introduce) - _ <- forbid(introduce ++ n.values.toSet) - } yield n + def findAndForbidNames(introduce: Set[String]): State[S, Map[String, String]] def forbid(names: Set[String]): State[S, Unit] - def forbidName(name: String): State[S, Unit] = - forbid(Set(name)) - def transformS[R](f: R => S, g: (R, S) => R): Mangler[R] = new Mangler[R] { - val getForbiddenNames: State[R, Set[String]] = - self.getForbiddenNames.transformS(f, g) - - def findNewNames(introduce: Set[String]): State[R, Map[String, String]] = - self.findNewNames(introduce).transformS(f, g) - def forbid(names: Set[String]): State[R, Unit] = self.forbid(names).transformS(f, g) + + def findAndForbidNames(introduce: Set[String]): State[R, Map[String, String]] = + self.findAndForbidNames(introduce).transformS(f, g) } } object Mangler { - def apply[S](implicit mangler: Mangler[S]): Mangler[S] = mangler + def apply[S](using mangler: Mangler[S]): Mangler[S] = mangler - implicit object Simple extends Mangler[Set[String]] { - val getForbiddenNames: State[Set[String], Set[String]] = State.get + given Mangler[ManglerState] with { + def findAndForbidNames(introduce: Set[String]): State[ManglerState, Map[String, String]] = + State.apply(_.findNewNames(introduce)) - def findNewNames(introduce: Set[String]): State[Set[String], Map[String, String]] = - getForbiddenNames.map(forbidden => - (forbidden intersect introduce).foldLeft(Map.empty[String, String]) { case (acc, name) => - acc + (name -> LazyList - .from(0) - .map(name + "-" + _) - .dropWhile(n => forbidden(n) || introduce(n) || acc.contains(n)) - .head) - } - ) - - def forbid(names: Set[String]): State[Set[String], Unit] = - State.modify(_ ++ names) + def forbid(names: Set[String]): State[ManglerState, Unit] = + State.modify(st => st.forbid(names)) } } diff --git a/model/inline/src/test/scala/aqua/model/inline/ManglerSpec.scala b/model/inline/src/test/scala/aqua/model/inline/ManglerSpec.scala new file mode 100644 index 00000000..df17745f --- /dev/null +++ b/model/inline/src/test/scala/aqua/model/inline/ManglerSpec.scala @@ -0,0 +1,80 @@ +package aqua.model.inline + +import aqua.mangler.ManglerState +import aqua.model.inline.state.Mangler +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class ManglerSpec extends AnyFlatSpec with Matchers { + + "mangler" should "rename right" in { + val mangler = Mangler[ManglerState] + + val results = for { + res <- mangler.findAndForbidNames(Set("first", "second")) + } yield res + + val res = results.runA(ManglerState()).value + res shouldBe Map() + } + + "mangler" should "rename right if already have renamed" in { + val mangler = Mangler[ManglerState] + + val results = for { + res1 <- mangler.findAndForbidNames(Set("first", "first-0", "first-1")) + res2 <- mangler.findAndForbidNames(Set("first")) + res3 <- mangler.findAndForbidNames(Set("first-0")) + res4 <- mangler.findAndForbidNames(Set("first-1")) + res5 <- mangler.findAndForbidNames(Set("first-2")) + } yield (res1, res2, res3, res4, res5) + + val (r1, r2, r3, r4, r5) = results.runA(ManglerState()).value + r1 shouldBe Map() + r2 shouldBe Map("first" -> "first-2") + r3 shouldBe Map("first-0" -> "first-0-0") + r4 shouldBe Map("first-1" -> "first-1-0") + r5 shouldBe Map("first-2" -> "first-2-0") + } + + "mangler" should "rename multiple times right" in { + val mangler = Mangler[ManglerState] + + val results = for { + res <- mangler.findAndForbidNames(Set("first", "second")) + res2 <- mangler.findAndForbidNames(Set("first", "second")) + res3 <- mangler.findAndForbidNames(Set("first")) + res4 <- mangler.findAndForbidNames(Set("first", "second")) + res5 <- mangler.findAndForbidNames(Set("second")) + } yield (res, res2, res3, res4, res5) + + val (r1, r2, r3, r4, r5) = results.runA(ManglerState()).value + r1 shouldBe Map() + r2 shouldBe Map("first" -> "first-0", "second" -> "second-0") + r3 shouldBe Map("first" -> "first-1") + r4 shouldBe Map("first" -> "first-2", "second" -> "second-1") + r5 shouldBe Map("second" -> "second-2") + } + + "mangler" should "forbid and rename right" in { + val mangler = Mangler[ManglerState] + + val results = for { + _ <- mangler.forbid(Set("first", "second")) + res1 <- mangler.findAndForbidNames(Set("first", "second")) + res2 <- mangler.findAndForbidNames(Set("first")) + _ <- mangler.forbid(Set("first")) + _ <- mangler.forbid(Set("first", "second")) + _ <- mangler.forbid(Set("second")) + res3 <- mangler.findAndForbidNames(Set("second")) + res4 <- mangler.findAndForbidNames(Set("second", "first")) + } yield (res1, res2, res3, res4) + + val (r1, r2, r3, r4) = results.runA(ManglerState()).value + r1 shouldBe Map("first" -> "first-0", "second" -> "second-0") + r2 shouldBe Map("first" -> "first-1") + r3 shouldBe Map("second" -> "second-1") + r4 shouldBe Map("first" -> "first-2", "second" -> "second-2") + } + +} diff --git a/model/inline/src/test/scala/aqua/model/inline/RawValueInlinerSpec.scala b/model/inline/src/test/scala/aqua/model/inline/RawValueInlinerSpec.scala index 8248986e..e7a25680 100644 --- a/model/inline/src/test/scala/aqua/model/inline/RawValueInlinerSpec.scala +++ b/model/inline/src/test/scala/aqua/model/inline/RawValueInlinerSpec.scala @@ -1,28 +1,28 @@ package aqua.model.inline -import aqua.model.inline.raw.{ApplyPropertiesRawInliner, StreamGateInliner} +import aqua.mangler.ManglerState import aqua.model.* +import aqua.model.inline.raw.StreamGateInliner import aqua.model.inline.state.InliningState -import aqua.raw.value.{ApplyPropertyRaw, FunctorRaw, IntoIndexRaw, LiteralRaw, VarRaw} -import aqua.types.* import aqua.raw.value.* - +import aqua.types.* import cats.Eval -import cats.data.NonEmptyMap -import cats.data.Chain -import cats.syntax.show.* -import cats.syntax.foldable.* +import cats.data.{Chain, NonEmptyMap} import cats.free.Cofree -import scala.collection.immutable.SortedMap -import scala.math +import cats.syntax.foldable.* +import org.scalatest.Inside import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers -import org.scalatest.Inside + +import scala.collection.immutable.SortedMap +import scala.math class RawValueInlinerSpec extends AnyFlatSpec with Matchers with Inside { import RawValueInliner.valueToModel + def toMangler(noNames: Set[String]) = ManglerState(noNames.map(_ -> 0).toMap) + def join(stream: VarModel, size: ValueModel) = stream match { case VarModel( @@ -188,7 +188,7 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers with Inside { "raw value inliner" should "desugarize a single non-recursive raw value" in { // x[y] valueToModel[InliningState](`raw x[y]`) - .runA(InliningState(noNames = Set("x", "y"))) + .runA(InliningState(noNames = toMangler(Set("x", "y")))) .value shouldBe ( VarModel( "x", @@ -200,7 +200,6 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers with Inside { // TODO: unignore and fix after stream restrictions will be implemented ignore /*"raw value inliner"*/ should "unfold an IntoField PropertyModel" in { - import aqua.model.inline.state.Mangler.Simple // a.field1.field2 valueToModel[InliningState](`raw res.c`) .runA( @@ -222,7 +221,7 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers with Inside { val (resVal, resTree) = valueToModel[InliningState]( `raw x[ys[0]]` ) - .runA(InliningState(noNames = Set("x", "ys"))) + .runA(InliningState(noNames = toMangler(Set("x", "ys")))) .value resVal should be( @@ -250,7 +249,9 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers with Inside { it should "desugarize properties with functors x[ys[ys.length]][2] and make proper flattener tags" in { val (resVal, resTree) = valueToModel[InliningState]( `x[xs[ys.length]][xss[yss.length]]` - ).runA(InliningState(noNames = Set("x", "ys", "xs", "yss", "xss"))).value + ).runA( + InliningState(noNames = toMangler(Set("x", "ys", "xs", "yss", "xss"))) + ).value resVal should be( VarModel( @@ -325,7 +326,7 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers with Inside { val (resVal, resTree) = valueToModel[InliningState]( `raw x[ys[0]][ys[1]]` ) - .runA(InliningState(noNames = Set("x", "ys"))) + .runA(InliningState(noNames = toMangler(Set("x", "ys")))) .value resVal should be( @@ -371,7 +372,7 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers with Inside { IntoIndexRaw(idxRaw, ScalarType.string) ) - val initState = InliningState(noNames = Set("x", "ys")) + val initState = InliningState(noNames = toMangler(Set("x", "ys"))) // Here retrieve how size is inlined val (afterSizeState, (sizeModel, sizeTree)) = @@ -420,7 +421,7 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers with Inside { ) val (resVal, resTree) = valueToModel[InliningState](streamWithProps) - .runA(InliningState(noNames = Set("x", "ys"))) + .runA(InliningState(noNames = toMangler(Set("x", "ys")))) .value } @@ -428,7 +429,7 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers with Inside { val (resVal, resTree) = valueToModel[InliningState]( `raw x[zs[ys[0]]][ys[1]]` ) - .runA(InliningState(noNames = Set("x", "ys", "zs"))) + .runA(InliningState(noNames = toMangler(Set("x", "ys", "zs")))) .value // This is x[zs-0][ys-0] diff --git a/model/raw/src/main/scala/aqua/raw/RawContext.scala b/model/raw/src/main/scala/aqua/raw/RawContext.scala index 9f26d028..6f4a5f2b 100644 --- a/model/raw/src/main/scala/aqua/raw/RawContext.scala +++ b/model/raw/src/main/scala/aqua/raw/RawContext.scala @@ -10,7 +10,6 @@ import cats.data.Chain import cats.data.NonEmptyMap import cats.syntax.monoid.* import cats.syntax.option.* - import scala.collection.immutable.SortedMap /** diff --git a/parser/src/main/scala/aqua/parser/lift/FileSpan.scala b/parser/src/main/scala/aqua/parser/lift/FileSpan.scala index e4b7613a..ea991e09 100644 --- a/parser/src/main/scala/aqua/parser/lift/FileSpan.scala +++ b/parser/src/main/scala/aqua/parser/lift/FileSpan.scala @@ -15,7 +15,8 @@ case class FileSpan(name: String, locationMap: Eval[LocationMap], span: Span) { * @return FileSpan.Focus */ def focus(ctx: Int): Option[FileSpan.Focus] = - span.focus(locationMap.value, ctx).map(FileSpan.Focus(name, locationMap, ctx, _)) + span. + focus(locationMap.value, ctx).map(FileSpan.Focus(name, locationMap, ctx, _)) override def hashCode(): Int = (name, span).hashCode() diff --git a/semantics/src/main/scala/aqua/semantics/CompilerState.scala b/semantics/src/main/scala/aqua/semantics/CompilerState.scala index 1ea49212..ef7783d5 100644 --- a/semantics/src/main/scala/aqua/semantics/CompilerState.scala +++ b/semantics/src/main/scala/aqua/semantics/CompilerState.scala @@ -1,12 +1,12 @@ package aqua.semantics +import aqua.mangler.ManglerState import aqua.parser.lexer.Token import aqua.raw.Raw import aqua.raw.RawContext import aqua.semantics.rules.abilities.AbilitiesState import aqua.semantics.rules.definitions.DefinitionsState import aqua.semantics.rules.locations.LocationsState -import aqua.semantics.rules.mangler.ManglerState import aqua.semantics.rules.names.NamesState import aqua.semantics.rules.report.ReportState import aqua.semantics.rules.types.TypesState diff --git a/semantics/src/main/scala/aqua/semantics/Semantics.scala b/semantics/src/main/scala/aqua/semantics/Semantics.scala index bf76ea63..9eebb9aa 100644 --- a/semantics/src/main/scala/aqua/semantics/Semantics.scala +++ b/semantics/src/main/scala/aqua/semantics/Semantics.scala @@ -1,9 +1,8 @@ package aqua.semantics import aqua.parser.Ast -import aqua.semantics.SemanticError -import cats.data.{Chain, EitherNec, EitherT, NonEmptyChain, ValidatedNec, Writer} +import cats.data.{Chain, EitherT, NonEmptyChain, Writer} trait Semantics[S[_], C] { diff --git a/semantics/src/main/scala/aqua/semantics/expr/func/ServiceIdSem.scala b/semantics/src/main/scala/aqua/semantics/expr/func/ServiceIdSem.scala index 7274c844..32dfc65a 100644 --- a/semantics/src/main/scala/aqua/semantics/expr/func/ServiceIdSem.scala +++ b/semantics/src/main/scala/aqua/semantics/expr/func/ServiceIdSem.scala @@ -31,11 +31,11 @@ class ServiceIdSem[S[_]](val expr: ServiceIdExpr[S]) extends AnyVal { ) serviceType <- EitherT.fromOptionF( T.resolveServiceType(expr.service), - Raw.error("Can not resolve service type") + Raw.error("Cannot resolve service type") ) name <- EitherT.fromOptionF( A.renameService(expr.service), - Raw.error("Can not set service ID") + Raw.error("Cannot set service ID") ) _ <- EitherT.liftF( N.derive( diff --git a/semantics/src/main/scala/aqua/semantics/rules/locations/LocationsState.scala b/semantics/src/main/scala/aqua/semantics/rules/locations/LocationsState.scala index dd11b98e..d2178fe2 100644 --- a/semantics/src/main/scala/aqua/semantics/rules/locations/LocationsState.scala +++ b/semantics/src/main/scala/aqua/semantics/rules/locations/LocationsState.scala @@ -21,8 +21,9 @@ case class LocationsState[S[_]]( name: String, token: Token[S] ): List[VariableInfo[S]] = { - if (!vars.exists(_.definition.name == name)) - logger.error(s"Unexpected. Cannot add occurrence for $name") + // TODO: this code lasts too long, but we can find errors in it. + // if (!vars.exists(_.definition.name == name)) + // logger.error(s"Unexpected. Cannot add occurrence for $name") vars.updateFirst(_.definition.name == name, v => v.copy(occurrences = token +: v.occurrences)) } diff --git a/semantics/src/main/scala/aqua/semantics/rules/mangler/ManglerInterpreter.scala b/semantics/src/main/scala/aqua/semantics/rules/mangler/ManglerInterpreter.scala index 0eff5d80..ef97b0a6 100644 --- a/semantics/src/main/scala/aqua/semantics/rules/mangler/ManglerInterpreter.scala +++ b/semantics/src/main/scala/aqua/semantics/rules/mangler/ManglerInterpreter.scala @@ -1,27 +1,17 @@ package aqua.semantics.rules.mangler +import aqua.mangler.ManglerState + import cats.data.State import monocle.Lens -import monocle.macros.GenLens class ManglerInterpreter[X](using lens: Lens[X, ManglerState] ) extends ManglerAlgebra[State[X, *]] { override def rename(name: String): State[X, String] = - for { - s <- get - newName = LazyList - .from(0) - .map(i => s"$name-$i") - .dropWhile(s.isForbidden) - .head - _ <- modify(_.forbid(newName)) - } yield newName + apply(_.forbidAndRename(name)) - private lazy val get: State[X, ManglerState] = - State.get[X].map(lens.get) - - private def modify(f: ManglerState => ManglerState): State[X, Unit] = - State.modify[X](lens.modify(f)) + private def apply[A](f: ManglerState => (ManglerState, A)): State[X, A] = + State.apply(lens.modifyF(f andThen (_.swap)) andThen (_.swap)) } diff --git a/semantics/src/main/scala/aqua/semantics/rules/mangler/ManglerState.scala b/semantics/src/main/scala/aqua/semantics/rules/mangler/ManglerState.scala deleted file mode 100644 index 7d7f5dca..00000000 --- a/semantics/src/main/scala/aqua/semantics/rules/mangler/ManglerState.scala +++ /dev/null @@ -1,24 +0,0 @@ -package aqua.semantics.rules.mangler - -import cats.kernel.Monoid - -final case class ManglerState( - forbidden: Set[String] = Set.empty -) { - - def isForbidden(name: String): Boolean = - forbidden.contains(name) - - def forbid(name: String): ManglerState = - copy(forbidden = forbidden + name) -} - -object ManglerState { - - given Monoid[ManglerState] with { - override val empty: ManglerState = ManglerState() - - override def combine(x: ManglerState, y: ManglerState): ManglerState = - ManglerState(forbidden = x.forbidden ++ y.forbidden) - } -} diff --git a/utils/logging/src/main/scala/aqua/logging/LogFormatter.scala b/utils/logging/src/main/scala/aqua/logging/LogFormatter.scala index 36c29a62..62cacf1b 100644 --- a/utils/logging/src/main/scala/aqua/logging/LogFormatter.scala +++ b/utils/logging/src/main/scala/aqua/logging/LogFormatter.scala @@ -6,10 +6,10 @@ import scribe.{Level, Logger} object LogFormatter { val formatter: Formatter = - formatter"$date ${string("[")}$levelColored${string("]")} $messages$mdc" + formatter"$date $timeStamp ${string("[")}$levelColored${string("]")} $messages$mdc" val formatterWithFilename: Formatter = - formatter"$date $fileName ${string("[")}$levelColored${string("]")} $messages$mdc" + formatter"$date $timeStamp $fileName ${string("[")}$levelColored${string("]")} $messages$mdc" def initLogger(level: Option[Level]): Logger = { scribe.Logger.root diff --git a/utils/mangler/src/main/scala/aqua/mangler/ManglerState.scala b/utils/mangler/src/main/scala/aqua/mangler/ManglerState.scala new file mode 100644 index 00000000..2cad6086 --- /dev/null +++ b/utils/mangler/src/main/scala/aqua/mangler/ManglerState.scala @@ -0,0 +1,65 @@ +package aqua.mangler + +import cats.Monoid + +import scala.annotation.tailrec + +case class ManglerState(namesNumbers: Map[String, Int] = Map.empty) { + + private def genName(name: String, n: Int) = + s"$name-$n" + + // find unique names that have not yet been used + def findNewNames(introduce: Set[String]): (ManglerState, Map[String, String]) = { + introduce.foldLeft(this, Map.empty[String, String]) { + case ((state, newNames), name) => + val namesNumbers = state.namesNumbers + if (!namesNumbers.contains(name)) { + val newState = state.copy( + namesNumbers = namesNumbers + .updated(name, 0) + ) + + (newState, newNames) + } else { + val (newNumber, newName) = LazyList + .from(namesNumbers.getOrElse(name, 0)) + .map(n => n -> genName(name, n)) + .dropWhile { case (_, newName) => + namesNumbers.contains(newName) + } + .head + val newState = copy( + namesNumbers = namesNumbers + .updated(name, newNumber + 1) + .updated(newName, 0) + ) + + (newState, newNames + (name -> newName)) + } + } + } + + // add names to used list + def forbid(names: Set[String]): ManglerState = { + val newLastNumbers = names.map(n => n -> namesNumbers.getOrElse(n, 0)).toMap + copy(namesNumbers = newLastNumbers ++ namesNumbers) + } + + // forbid name and return unique + def forbidAndRename(name: String): (ManglerState, String) = { + val set = Set(name) + val (newState, newNames) = forbid(set).findNewNames(set) + (newState, newNames(name)) + } +} + +object ManglerState { + + given Monoid[ManglerState] with { + override val empty: ManglerState = ManglerState() + + override def combine(x: ManglerState, y: ManglerState): ManglerState = + ManglerState(namesNumbers = x.namesNumbers ++ y.namesNumbers) + } +}