Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(compiler): Make on propagate errors [fixes LNG-203] #788

Merged
merged 11 commits into from
Jul 12, 2023
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions api/api/src/main/scala/aqua/api/AquaAPIConfig.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@ case class AquaAPIConfig(
targetType: TargetType = TargetType.AirType,
logLevel: String = "info",
constants: List[String] = Nil,
noXor: Boolean = false,
noXor: Boolean = false, // TODO: Remove
noRelay: Boolean = false,
tracing: Boolean = false
) {

def getTransformConfig: TransformConfig = {
val config = TransformConfig(
wrapWithXor = !noXor,
tracing = Option.when(tracing)(TransformConfig.TracingConfig.default)
)

Expand Down
5 changes: 5 additions & 0 deletions backend/air/src/main/scala/aqua/backend/air/Air.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ object Keyword {

case object Ap extends Keyword("ap")

case object Fail extends Keyword("fail")

case object Canon extends Keyword("canon")

case object Seq extends Keyword("seq")
Expand Down Expand Up @@ -110,6 +112,8 @@ object Air {

case class Ap(op: DataView, result: String) extends Air(Keyword.Ap)

case class Fail(op: DataView) extends Air(Keyword.Fail)

case class Canon(op: DataView, peerId: DataView, result: String) extends Air(Keyword.Canon)

case class Comment(comment: String, air: Air) extends Air(Keyword.NA)
Expand Down Expand Up @@ -143,6 +147,7 @@ object Air {
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.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"
Expand Down
11 changes: 11 additions & 0 deletions backend/air/src/main/scala/aqua/backend/air/AirGen.scala
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ object AirGen extends Logging {
ApGen(valueToData(operand), exportToString(exportTo))
)

case FailRes(operand) =>
Eval.later(
FailGen(valueToData(operand))
)

case CanonRes(operand, peerId, exportTo) =>
Eval.later(
CanonGen(valueToData(operand), valueToData(peerId), exportToString(exportTo))
Expand Down Expand Up @@ -164,6 +169,12 @@ case class ApGen(operand: DataView, result: String) extends AirGen {
Air.Ap(operand, result)
}

case class FailGen(operand: DataView) extends AirGen {

override def generate: Air =
Air.Fail(operand)
}

case class CanonGen(operand: DataView, peerId: DataView, result: String) extends AirGen {

override def generate: Air =
Expand Down
12 changes: 6 additions & 6 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ lazy val cliJS = cli.js
.settings(
Compile / fastOptJS / artifactPath := baseDirectory.value / "../../cli-npm" / "aqua.js",
Compile / fullOptJS / artifactPath := baseDirectory.value / "../../cli-npm" / "aqua.js",
scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.ESModule)),
scalaJSUseMainModuleInitializer := true
scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.ESModule)),
scalaJSUseMainModuleInitializer := true
)
.dependsOn(`js-exports`, `js-imports`)

Expand Down Expand Up @@ -155,9 +155,9 @@ lazy val `aqua-apiJS` = `aqua-api`.js
.settings(
Compile / fastOptJS / artifactPath := baseDirectory.value / "../../api-npm" / "aqua-api.js",
Compile / fullOptJS / artifactPath := baseDirectory.value / "../../api-npm" / "aqua-api.js",
scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.CommonJSModule)),
scalaJSUseMainModuleInitializer := true,
Test / test := {}
scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.CommonJSModule)),
scalaJSUseMainModuleInitializer := true,
Test / test := {}
)
.enablePlugins(ScalaJSPlugin)
.dependsOn(`js-exports`)
Expand Down Expand Up @@ -252,7 +252,7 @@ lazy val compiler = crossProject(JVMPlatform, JSPlatform)
.crossType(CrossType.Pure)
.in(file("compiler"))
.settings(commons: _*)
.dependsOn(semantics, linker, backend, transform % Test, res % "test->test")
.dependsOn(semantics, linker, backend, transform % "test->test", res % "test->test")

lazy val backend = crossProject(JVMPlatform, JSPlatform)
.withoutSuffixFor(JVMPlatform)
Expand Down
16 changes: 9 additions & 7 deletions cli/cli/.js/src/main/scala/aqua/run/RunOpts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import cats.syntax.applicative.*
import cats.syntax.apply.*
import cats.syntax.flatMap.*
import cats.syntax.functor.*
import cats.{Id, Monad, ~>}
import cats.{~>, Id, Monad}
import com.monovore.decline.{Command, Opts}
import fs2.io.file.{Files, Path}
import scribe.Logging
Expand All @@ -45,14 +45,15 @@ object RunOpts extends Logging {
): TransformConfig = {
val tc = TransformConfig(
constants =
onPeer.map(s => ConstantRaw(OnPeerConst, LiteralRaw.quote(s), false)).toList ++ constants,
wrapWithXor = !noXor
onPeer.map(s => ConstantRaw(OnPeerConst, LiteralRaw.quote(s), false)).toList ++ constants
)
tc.copy(relayVarName = tc.relayVarName.filterNot(_ => noRelay))
}

def runOptsCompose[F[_]: Files: Concurrent]
: Opts[F[ValidatedNec[String, (Option[AquaPath], List[Path], FuncWithData, Option[NonEmptyList[JsonService]], List[String])]]] = {
def runOptsCompose[F[_]: Files: Concurrent]: Opts[F[ValidatedNec[
String,
(Option[AquaPath], List[Path], FuncWithData, Option[NonEmptyList[JsonService]], List[String])
]]] = {
(
AppOpts.wrapWithOption(AppOpts.inputOpts[F]),
AppOpts.importOpts[F],
Expand All @@ -72,8 +73,9 @@ object RunOpts extends Logging {
.getOrElse(validNec[String, Option[NonEmptyList[JsonService]]](None).pure[F])
pluginsPathsV <- pluginsOp.getOrElse(validNec[String, List[String]](Nil).pure[F])
} yield {
(inputV, importV, funcWithArgsV, jsonServiceV, pluginsPathsV).mapN { case (i, im, f, j, p) =>
(i, im, f, j, p)
(inputV, importV, funcWithArgsV, jsonServiceV, pluginsPathsV).mapN {
case (i, im, f, j, p) =>
(i, im, f, j, p)
}
}
}
Expand Down
12 changes: 2 additions & 10 deletions cli/cli/.js/src/main/scala/aqua/script/ScriptOpts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,7 @@ import aqua.raw.ops.{Call, CallArrowRawTag}
import aqua.raw.value.{LiteralRaw, ValueRaw, VarRaw}
import aqua.res.{AquaRes, FuncRes}
import aqua.run.RunOpts.logger
import aqua.run.{
CliFunc,
FuncCompiler,
GeneralOptions,
GeneralOpts,
RunCommand,
RunConfig,
RunOpts
}
import aqua.run.{CliFunc, FuncCompiler, GeneralOptions, GeneralOpts, RunCommand, RunConfig, RunOpts}
import aqua.types.{ArrowType, LiteralType, NilType, ScalarType}
import cats.data.*
import cats.data.Validated.{invalid, invalidNec, valid, validNec, validNel}
Expand Down Expand Up @@ -126,7 +118,7 @@ object ScriptOpts extends Logging {
imports: List[Path],
funcWithArgs: FuncWithLiteralArgs
): F[ValidatedNec[String, String]] = {
val tConfig = TransformConfig(relayVarName = None, wrapWithXor = false)
val tConfig = TransformConfig(relayVarName = None)
val funcCompiler =
new FuncCompiler[F](
Option(RelativePath(input)),
Expand Down
2 changes: 1 addition & 1 deletion cli/cli/.jvm/src/main/scala/aqua/Test.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ object Test extends IOApp.Simple {
List(Path("./aqua")),
Option(Path("./target")),
TypeScriptBackend(false, "IFluenceClient$$"),
TransformConfig(wrapWithXor = false),
TransformConfig(),
false
)
.map {
Expand Down
3 changes: 1 addition & 2 deletions cli/cli/src/main/scala/aqua/AquaCli.scala
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ object AquaCli extends IOApp with Logging {
compileToAir,
compileToJs,
noRelay,
noXorWrapper,
noXorWrapper, // TODO: Remove
tracing,
isOldFluenceJs,
wrapWithOption(helpOpt),
Expand Down Expand Up @@ -137,7 +137,6 @@ object AquaCli extends IOApp with Logging {
else TypescriptTarget
val bc = {
val bc = TransformConfig(
wrapWithXor = !noXor,
constants = constants,
tracing = Option.when(tracingEnabled)(TransformConfig.TracingConfig.default)
)
Expand Down
106 changes: 54 additions & 52 deletions compiler/src/test/scala/aqua/compiler/AquaCompilerSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import aqua.model.{
ValueModel,
VarModel
}
import aqua.model.transform.ModelBuilder
import aqua.model.transform.TransformConfig
import aqua.model.transform.Transform
import aqua.parser.ParserError
Expand Down Expand Up @@ -42,6 +43,7 @@ import cats.instances.string.*
import cats.syntax.show.*

class AquaCompilerSpec extends AnyFlatSpec with Matchers {
import ModelBuilder.*

private def aquaSource(src: Map[String, String], imports: Map[String, String]) = {
new AquaSources[Id, String, String] {
Expand Down Expand Up @@ -150,8 +152,8 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers {
ctxs.length should be(1)
val ctx = ctxs.headOption.get

val aquaRes =
Transform.contextRes(ctx, TransformConfig(wrapWithXor = false))
val transformCfg = TransformConfig()
val aquaRes = Transform.contextRes(ctx, transformCfg)

val Some(exec) = aquaRes.funcs.find(_.funcName == "exec")

Expand All @@ -167,47 +169,49 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers {
SeqRes.wrap(
getDataSrv("-relay-", ScalarType.string),
getDataSrv(peers.name, peers.`type`),
RestrictionRes(results.name, resultsType).wrap(
SeqRes.wrap(
ParRes.wrap(
FoldRes(peer.name, peers, Some(ForModel.NeverMode)).wrap(
ParRes.wrap(
// better if first relay will be outside `for`
SeqRes.wrap(
through(ValueModel.fromRaw(relay)),
CallServiceRes(
LiteralModel.fromRaw(LiteralRaw.quote("op")),
"identity",
CallRes(
LiteralModel.fromRaw(LiteralRaw.quote("hahahahah")) :: Nil,
Some(CallModel.Export(results.name, results.`type`))
XorRes.wrap(
RestrictionRes(results.name, resultsType).wrap(
SeqRes.wrap(
ParRes.wrap(
FoldRes(peer.name, peers, Some(ForModel.NeverMode)).wrap(
ParRes.wrap(
XorRes.wrap(
// better if first relay will be outside `for`
SeqRes.wrap(
through(ValueModel.fromRaw(relay)),
CallServiceRes(
LiteralModel.fromRaw(LiteralRaw.quote("op")),
"identity",
CallRes(
LiteralModel.fromRaw(LiteralRaw.quote("hahahahah")) :: Nil,
Some(CallModel.Export(results.name, results.`type`))
),
peer
).leaf,
through(ValueModel.fromRaw(relay)),
through(initPeer)
),
peer
).leaf,
through(ValueModel.fromRaw(relay)),
through(initPeer)
),
NextRes(peer.name).leaf
SeqRes.wrap(
through(ValueModel.fromRaw(relay)),
through(initPeer),
failLastErrorRes
)
),
NextRes(peer.name).leaf
)
)
)
),
join(results, LiteralModel.fromRaw(LiteralRaw.number(2))),
CanonRes(results, init, CallModel.Export(canonResult.name, canonResult.`type`)).leaf,
ApRes(
canonResult,
CallModel.Export(flatResult.name, flatResult.`type`)
).leaf
)
),
CallServiceRes(
LiteralModel.fromRaw(LiteralRaw.quote("callbackSrv")),
"response",
CallRes(
flatResult :: Nil,
None
),
join(results, LiteralModel.fromRaw(LiteralRaw.number(2))),
CanonRes(results, init, CallModel.Export(canonResult.name, canonResult.`type`)).leaf,
ApRes(
canonResult,
CallModel.Export(flatResult.name, flatResult.`type`)
).leaf
)
),
initPeer
).leaf
errorCall(transformCfg, 0, initPeer)
),
respCall(transformCfg, flatResult, initPeer)
)

exec.body.equalsOrShowDiff(expected) shouldBe (true)
Expand Down Expand Up @@ -267,8 +271,8 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers {
ctxs.length should be(1)
val ctx = ctxs.headOption.get

val aquaRes =
Transform.contextRes(ctx, TransformConfig(wrapWithXor = false, relayVarName = None))
val transformCfg = TransformConfig(relayVarName = None)
val aquaRes = Transform.contextRes(ctx, transformCfg)

val Some(funcWrap) = aquaRes.funcs.find(_.funcName == "wrap")
val Some(barfoo) = aquaRes.funcs.find(_.funcName == "barfoo")
Expand All @@ -278,8 +282,8 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers {
val resCanonVM = VarModel("-res-fix-0", CanonStreamType(ScalarType.string))
val resFlatVM = VarModel("-res-flat-0", ArrayType(ScalarType.string))

barfoo.body.equalsOrShowDiff(
SeqRes.wrap(
val expected = SeqRes.wrap(
XorRes.wrap(
RestrictionRes(resVM.name, resStreamType).wrap(
SeqRes.wrap(
// res <- foo()
Expand All @@ -305,14 +309,12 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers {
).leaf
)
),
CallServiceRes(
LiteralModel.fromRaw(LiteralRaw.quote("callbackSrv")),
"response",
CallRes(resFlatVM :: Nil, None),
LiteralModel.fromRaw(ValueRaw.InitPeerId)
).leaf
)
) should be(true)
errorCall(transformCfg, 0, initPeer)
),
respCall(transformCfg, resFlatVM, initPeer)
)

barfoo.body.equalsOrShowDiff(expected) should be(true)

}
}
39 changes: 39 additions & 0 deletions integration-tests/aqua/examples/onErrorPropagation.aqua
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
service Test("test-service"):
fail(err: string)

func onPropagate(peer: string, relay: string) -> u16:
res: *u16
on peer via relay:
res <<- 0 + 1
Test.fail("propagated error")
res <<- 0 + 2

join res[3] -- Unreachable

<- res[3]

func nestedOnPropagate(peer: string, relay: string, iPeer: string, iRelay: string, friend: string) -> u16:
res: *u16
on iPeer via iRelay:
res <<- 40 + 2
on friend:
res <<- 2 + 40
on peer via relay:
Test.fail("propagated error")
res <<- 30 + 7

join res[3] -- Unreachable

<- res[3]

func seqOnPropagate(peer: string, relay: string, iPeer: string, iRelay: string) -> u16:
res: *u16
on iPeer via iRelay:
res <<- 40 + 2
on peer via relay:
Test.fail("propagated error")
res <<- 30 + 7

join res[2] -- Unreachable

<- res[2]
Loading