From e6c5d0039f1556c9adafa1714ae048a25f252d29 Mon Sep 17 00:00:00 2001 From: Dima Date: Wed, 17 Apr 2024 15:40:19 +0300 Subject: [PATCH] fix(compiler): Return error if SemanticError occures [LNG-356] (#1126) --- aqua-src/antithesis.aqua | 33 ++++- .../src/main/scala/aqua/lsp/LspContext.scala | 6 +- .../main/scala/aqua/lsp/LspSemantics.scala | 15 +- .../src/test/scala/aqua/lsp/AquaLSPSpec.scala | 140 ++++++++++++++++-- .../src/main/scala/aqua/raw/ErroredPart.scala | 10 ++ .../src/main/scala/aqua/raw/RawContext.scala | 4 +- .../scala/aqua/semantics/RawSemantics.scala | 1 - .../aqua/semantics/expr/AbilitySem.scala | 15 +- .../scala/aqua/semantics/expr/AliasSem.scala | 5 +- .../aqua/semantics/expr/ConstantSem.scala | 6 +- .../aqua/semantics/expr/DataStructSem.scala | 5 +- .../aqua/semantics/expr/ServiceSem.scala | 79 +++++----- .../aqua/semantics/expr/func/FuncSem.scala | 4 +- 13 files changed, 216 insertions(+), 107 deletions(-) create mode 100644 model/raw/src/main/scala/aqua/raw/ErroredPart.scala diff --git a/aqua-src/antithesis.aqua b/aqua-src/antithesis.aqua index 2f7021fc..7e0f6d00 100644 --- a/aqua-src/antithesis.aqua +++ b/aqua-src/antithesis.aqua @@ -1,10 +1,29 @@ -aqua Main +aqua Job declares * -export main +export aaa, bbb -func reward(amount: ?u32) -> u32: - result = ?[amount! / 10, 0] - <- result! +data Peer: + id: string -func main(a: u32) -> u32: - <- reward(?[a]) \ No newline at end of file +func aaa() -> Peer: + peer1 = Peer(id = "123") + peer2 = Peer(id = peer1.id) + <- peer2 + +data BrokenStruct: + fff: UnknownType + +alias BrokenAlias: str3ng + +ability BrokenAbility: + fff: str4ng + +const BROKEN_CONST = UNKNOWN_CONST + +func bbb() -> string: + <- 323 + +func ccc() -> Peer: + peer1 = Peer(id = "123") + peer2 = Peer(id = peer1.id) + <- peer2 \ No newline at end of file diff --git a/language-server/language-server-api/src/main/scala/aqua/lsp/LspContext.scala b/language-server/language-server-api/src/main/scala/aqua/lsp/LspContext.scala index e531d9f7..19679aec 100644 --- a/language-server/language-server-api/src/main/scala/aqua/lsp/LspContext.scala +++ b/language-server/language-server-api/src/main/scala/aqua/lsp/LspContext.scala @@ -33,9 +33,9 @@ object LspContext { def blank[S[_]]: LspContext[S] = LspContext[S](raw = RawContext()) given [S[_]]: Monoid[LspContext[S]] with { - override def empty = blank[S] + override def empty: LspContext[S] = blank[S] - override def combine(x: LspContext[S], y: LspContext[S]) = + override def combine(x: LspContext[S], y: LspContext[S]): LspContext[S] = LspContext[S]( raw = x.raw |+| y.raw, abDefinitions = x.abDefinitions ++ y.abDefinitions, @@ -52,7 +52,7 @@ object LspContext { given [S[_]]: Picker[LspContext[S]] with { import aqua.semantics.header.Picker.* - override def blank: LspContext[S] = LspContext[S](Picker[RawContext].blank, Map.empty) + override def blank: LspContext[S] = LspContext.blank[S] override def exports(ctx: LspContext[S]): Map[String, Option[String]] = ctx.raw.exports override def isAbility(ctx: LspContext[S], name: String): Boolean = diff --git a/language-server/language-server-api/src/main/scala/aqua/lsp/LspSemantics.scala b/language-server/language-server-api/src/main/scala/aqua/lsp/LspSemantics.scala index f988996c..335ed8c5 100644 --- a/language-server/language-server-api/src/main/scala/aqua/lsp/LspSemantics.scala +++ b/language-server/language-server-api/src/main/scala/aqua/lsp/LspSemantics.scala @@ -2,21 +2,15 @@ package aqua.lsp import aqua.parser.Ast import aqua.parser.head.{ImportExpr, ImportFromExpr, UseExpr, UseFromExpr} -import aqua.parser.lexer.{LiteralToken, Token} +import aqua.parser.lexer.LiteralToken import aqua.raw.ConstantRaw +import aqua.semantics.* import aqua.semantics.header.Picker.* import aqua.semantics.rules.locations.LocationsState -import aqua.semantics.{CompilerState, RawSemantics, SemanticError, SemanticWarning, Semantics} -import cats.data.Validated.{Invalid, Valid} -import cats.data.{NonEmptyChain, ValidatedNec} -import cats.syntax.applicative.* -import cats.syntax.apply.* -import cats.syntax.either.* -import cats.syntax.flatMap.* -import cats.syntax.foldable.* +import cats.data.{EitherT, NonEmptyChain, Writer} import cats.syntax.functor.* -import cats.syntax.reducible.* +import cats.syntax.applicative.* import monocle.Lens import monocle.macros.GenLens @@ -74,7 +68,6 @@ class LspSemantics[S[_]]( warnings = state.warnings.toList ).pure[Result] } - // TODO: return as Eval .value } } diff --git a/language-server/language-server-api/src/test/scala/aqua/lsp/AquaLSPSpec.scala b/language-server/language-server-api/src/test/scala/aqua/lsp/AquaLSPSpec.scala index de349f23..b05e8dd3 100644 --- a/language-server/language-server-api/src/test/scala/aqua/lsp/AquaLSPSpec.scala +++ b/language-server/language-server-api/src/test/scala/aqua/lsp/AquaLSPSpec.scala @@ -3,10 +3,12 @@ package aqua.lsp import aqua.compiler.FileIdString.given_FileId_String import aqua.compiler.{AquaCompilerConf, AquaError, AquaSources} import aqua.parser.Parser +import aqua.parser.lexer.Token import aqua.parser.lift.Span import aqua.parser.lift.Span.S import aqua.raw.ConstantRaw import aqua.semantics.rules.locations.{DefinitionInfo, TokenLocation, VariableInfo} +import aqua.semantics.{RulesViolated, SemanticError} import aqua.types.* import cats.Id @@ -131,6 +133,16 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside { ee } + def insideError(err: SemanticError[S], str: String, pos: Int, code: String) = { + inside(err) { case RulesViolated(token, _) => + val span = token.unit._1 + val locatedOp = getByPosition(code, str, pos) + locatedOp shouldBe defined + val located = locatedOp.get + (span.startIndex, span.endIndex) shouldBe (located._1, located._2) + } + } + it should "return right tokens" in { val main = """aqua Import declares foo_wrapper, Ab, Str, useAbAndStruct, SOME_CONST @@ -153,6 +165,7 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside { | num(someVar) | OneMore fooResult | OneMore.more_call() + | <- "123" | |ability Ab: | someField: u32 @@ -362,26 +375,26 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside { | a: SomeAlias | |data SomeStruct: - | al: SomeAlias + | al11: SomeAlias | nested: NestedStruct | |ability SomeAbility: | someStr: SomeStruct | nested: NestedStruct - | al: SomeAlias - | someFunc(ss: SomeStruct, nest: NestedStruct, al: SomeAlias) -> NestedStruct, SomeStruct, SomeAlias + | al11: SomeAlias + | someFunc(ss: SomeStruct, nest: NestedStruct, al11: SomeAlias) -> NestedStruct, SomeStruct, SomeAlias | |service Srv("a"): - | check(ss: SomeStruct, nest: NestedStruct, al: SomeAlias) -> NestedStruct + | check(ss: SomeStruct, nest: NestedStruct, al11: SomeAlias) -> NestedStruct | check2() -> SomeStruct | check3() -> SomeAlias | |func withAb{SomeAbility}() -> SomeStruct: - | Srv.check() + | Srv.check(SomeAbility.someStr, SomeAbility.nested, SomeAbility.al11) | Srv.check2() | <- SomeAbility.someStr | - |func main(ss: SomeStruct, nest: NestedStruct, al: SomeAlias) -> string: + |func main(ss: SomeStruct, nest: NestedStruct, al11: SomeAlias) -> string: | Srv.check3() | <- "" |""".stripMargin @@ -394,11 +407,11 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside { val nestedType = StructType("NestedStruct", NonEmptyMap.of(("a", ScalarType.string))) val someStr = - StructType("SomeStruct", NonEmptyMap.of(("nested", nestedType), ("al", ScalarType.string))) + StructType("SomeStruct", NonEmptyMap.of(("nested", nestedType), ("al11", ScalarType.string))) val abFuncType = ArrowType( ProductType.labelled( - ("ss", someStr) :: ("nest", nestedType) :: ("al", ScalarType.string) :: Nil + ("ss", someStr) :: ("nest", nestedType) :: ("al11", ScalarType.string) :: Nil ), ProductType(nestedType :: someStr :: ScalarType.string :: Nil) ) @@ -407,7 +420,7 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside { NonEmptyMap.of( ("someStr", someStr), ("nested", nestedType), - ("al", ScalarType.string), + ("al11", ScalarType.string), ("someFunc", abFuncType) ) ) @@ -419,7 +432,7 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside { "check", ArrowType( ProductType.labelled( - ("ss", someStr) :: ("nest", nestedType) :: ("al", ScalarType.string) :: Nil + ("ss", someStr) :: ("nest", nestedType) :: ("al11", ScalarType.string) :: Nil ), ProductType(nestedType :: Nil) ) @@ -445,10 +458,14 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside { } res.checkTokenLoc(main, "SomeAbility", 0, someAb) shouldBe true - // from {SomeAbility} to 'ability SomeAbility' - res.checkLocations("SomeAbility", 0, 1, main) shouldBe true - // from 'SomeAbility.someStr' to {SomeAbility} - res.checkLocations("SomeAbility", 0, 2, main) shouldBe true + Range.inclusive(1, 5).foreach { n => + res.checkLocations("SomeAbility", 0, n, main) shouldBe true + } + + res.checkLocations("someStr", 0, 1, main) shouldBe true + res.checkLocations("someStr", 0, 2, main) shouldBe true + res.checkLocations("nested", 1, 2, main) shouldBe true + res.checkLocations("al11", 1, 4, main) shouldBe true res.checkTokenLoc(main, "Srv", 0, srvType) shouldBe true Range.inclusive(1, 3).foreach { n => @@ -538,4 +555,99 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside { res.checkLocations("Abilyy", 0, 2, main) shouldBe true res.checkLocations("Abilyy", 0, 3, main) shouldBe true } + + it should "return correct locations of errors on exported functions (LNG-356)" in { + val main = + """aqua Job declares * + | + |export aaa + | + |data Peer: + | id: string + | + |func aaa() -> string: + | peer = Pe2er(id = "123") + | <- peer.id""".stripMargin + val src = Map( + "index.aqua" -> main + ) + + val imports = Map.empty[String, String] + + val res = compile(src, imports).toEither.toOption.get.values.head + + val errors = res.errors + + insideError(errors.head, "Pe2er", 0, main) + insideError(errors(1), "peer", 1, main) + insideError(errors(2), "string", 1, main) + } + + it should "return correct locations in functions even if there is errors in other parts of a code" in { + val main = + """aqua Job declares * + | + |export aaa, bbb + | + |data Peer: + | id: string + | + |func aaa() -> Peer: + | peer1 = Peer(id = "123") + | peer2 = Peer(id = peer1.id) + | <- peer2 + | + |data BrokenStruct: + | fff: UnknownType1 + | + |alias BrokenAlias: UnknownType2 + | + |ability BrokenAbility: + | fff: UnknownType3 + | + |const BROKEN_CONST = UNKNOWN_CONST + | + |func bbb() -> string: + | <- 323 + | + |func ccc() -> Peer: + | peer1 = Peer(id = "123") + | peer2 = Peer(id = peer1.id) + | <- peer2 + | + |""".stripMargin + val src = Map( + "index.aqua" -> main + ) + + val imports = Map.empty[String, String] + + val res = compile(src, imports).toOption.get.values.head + val errors = res.errors + + // 'aaa' function + res.checkLocations("Peer", 0, 1, main) shouldBe true + res.checkLocations("Peer", 0, 2, main) shouldBe true + res.checkLocations("Peer", 0, 3, main) shouldBe true + res.checkLocations("peer1", 0, 1, main) shouldBe true + res.checkLocations("peer1", 0, 1, main) shouldBe true + + // 'ccc' function + res.checkLocations("Peer", 0, 4, main) shouldBe true + res.checkLocations("Peer", 0, 5, main) shouldBe true + res.checkLocations("Peer", 0, 6, main) shouldBe true + res.checkLocations("peer1", 2, 3, main) shouldBe true + res.checkLocations("peer1", 2, 3, main) shouldBe true + + // errors + insideError(errors.head, "UnknownType1", 0, main) + insideError(errors(1), "BrokenStruct", 0, main) + insideError(errors(2), "UnknownType2", 0, main) + insideError(errors(3), "UnknownType3", 0, main) + insideError(errors(4), "BrokenAbility", 0, main) + insideError(errors(5), "UNKNOWN_CONST", 0, main) + insideError(errors(6), "323", 0, main) + insideError(errors(7), "string", 1, main) + } + } diff --git a/model/raw/src/main/scala/aqua/raw/ErroredPart.scala b/model/raw/src/main/scala/aqua/raw/ErroredPart.scala new file mode 100644 index 00000000..62623789 --- /dev/null +++ b/model/raw/src/main/scala/aqua/raw/ErroredPart.scala @@ -0,0 +1,10 @@ +package aqua.raw + +import aqua.types.{BottomType, Type} + +// Part for structures that have errors inside but must be registered in context +case class ErroredPart(name: String) extends RawPart { + override def rawPartType: Type = BottomType + + override def rename(s: String): RawPart = copy(name = s) +} diff --git a/model/raw/src/main/scala/aqua/raw/RawContext.scala b/model/raw/src/main/scala/aqua/raw/RawContext.scala index 857b6377..741d5547 100644 --- a/model/raw/src/main/scala/aqua/raw/RawContext.scala +++ b/model/raw/src/main/scala/aqua/raw/RawContext.scala @@ -90,7 +90,7 @@ case class RawContext( parts.map { case (_, p) => p.name }.toList.toSet lazy val declaredNames: Set[String] = - allNames.filter(declares.contains) + allNames.intersect(declares) override def toString: String = s"""|module: ${module.getOrElse("unnamed")} @@ -109,7 +109,7 @@ object RawContext { override def empty: RawContext = blank - override def combine(x: RawContext, y: RawContext) = + override def combine(x: RawContext, y: RawContext): RawContext = RawContext( x.module orElse y.module, x.declares ++ y.declares, diff --git a/semantics/src/main/scala/aqua/semantics/RawSemantics.scala b/semantics/src/main/scala/aqua/semantics/RawSemantics.scala index 8787f570..89a9daa0 100644 --- a/semantics/src/main/scala/aqua/semantics/RawSemantics.scala +++ b/semantics/src/main/scala/aqua/semantics/RawSemantics.scala @@ -56,7 +56,6 @@ class RawSemantics[S[_]]( ) ) } - // TODO: return as Eval .value } } diff --git a/semantics/src/main/scala/aqua/semantics/expr/AbilitySem.scala b/semantics/src/main/scala/aqua/semantics/expr/AbilitySem.scala index a47f8b1f..1256e590 100644 --- a/semantics/src/main/scala/aqua/semantics/expr/AbilitySem.scala +++ b/semantics/src/main/scala/aqua/semantics/expr/AbilitySem.scala @@ -1,24 +1,15 @@ package aqua.semantics.expr import aqua.parser.expr.AbilityExpr -import aqua.raw.{Raw, TypeRaw} -import aqua.parser.lexer.{Name, NamedTypeToken} +import aqua.raw.{ErroredPart, Raw, TypeRaw} import aqua.semantics.Prog -import aqua.semantics.rules.ValuesAlgebra -import aqua.semantics.rules.abilities.AbilitiesAlgebra import aqua.semantics.rules.definitions.DefinitionsAlgebra -import aqua.semantics.rules.names.NamesAlgebra import aqua.semantics.rules.types.TypesAlgebra -import aqua.types.{AbilityType, ArrowType, Type} +import cats.Monad import cats.syntax.apply.* import cats.syntax.flatMap.* import cats.syntax.functor.* -import cats.syntax.applicative.* -import cats.syntax.semigroupal.* -import cats.syntax.traverse.* -import cats.Monad -import cats.data.{NonEmptyList, NonEmptyMap} class AbilitySem[S[_]](val expr: AbilityExpr[S]) extends AnyVal { @@ -32,7 +23,7 @@ class AbilitySem[S[_]](val expr: AbilityExpr[S]) extends AnyVal { fields = defs.view.mapValues(d => d.name -> d.`type`).toMap abilityType <- T.defineAbilityType(expr.name, fields) result = abilityType.map(st => TypeRaw(expr.name.value, st)) - } yield result.getOrElse(Raw.error("Ability types unresolved")) + } yield result.getOrElse(ErroredPart(expr.name.value)) ) } } diff --git a/semantics/src/main/scala/aqua/semantics/expr/AliasSem.scala b/semantics/src/main/scala/aqua/semantics/expr/AliasSem.scala index bf709462..fd5709cd 100644 --- a/semantics/src/main/scala/aqua/semantics/expr/AliasSem.scala +++ b/semantics/src/main/scala/aqua/semantics/expr/AliasSem.scala @@ -1,10 +1,9 @@ package aqua.semantics.expr import aqua.parser.expr.AliasExpr -import aqua.raw.{Raw, TypeRaw} +import aqua.raw.{ErroredPart, Raw, TypeRaw} import aqua.semantics.Prog import aqua.semantics.rules.types.TypesAlgebra - import cats.Applicative import cats.Monad import cats.syntax.flatMap.* @@ -15,6 +14,6 @@ class AliasSem[S[_]](val expr: AliasExpr[S]) extends AnyVal { def program[Alg[_]: Monad](using T: TypesAlgebra[S, Alg]): Prog[Alg, Raw] = T.resolveType(expr.target).flatMap { case Some(t) => T.defineAlias(expr.name, t) as (TypeRaw(expr.name.value, t): Raw) - case None => Applicative[Alg].pure(Raw.error("Alias type unresolved")) + case None => Applicative[Alg].pure(ErroredPart(expr.name.value)) } } diff --git a/semantics/src/main/scala/aqua/semantics/expr/ConstantSem.scala b/semantics/src/main/scala/aqua/semantics/expr/ConstantSem.scala index 32fa71ab..fea616f5 100644 --- a/semantics/src/main/scala/aqua/semantics/expr/ConstantSem.scala +++ b/semantics/src/main/scala/aqua/semantics/expr/ConstantSem.scala @@ -1,7 +1,7 @@ package aqua.semantics.expr import aqua.parser.expr.ConstantExpr -import aqua.raw.{ConstantRaw, Raw} +import aqua.raw.{ConstantRaw, ErroredPart, Raw} import aqua.semantics.Prog import aqua.semantics.rules.ValuesAlgebra import aqua.semantics.rules.names.NamesAlgebra @@ -27,12 +27,12 @@ class ConstantSem[S[_]](val expr: ConstantExpr[S]) extends AnyVal { case true => Raw.empty(s"Constant with name ${expr.name} was already defined, skipping") case false => - Raw.error(s"Constant with name ${expr.name} was defined with different type") + ErroredPart(expr.name.value) } case (Some(_), _, _) => Raw.error(s"Name '${expr.name.value}' was already defined").pure[Alg] case (_, None, _) => - Raw.error(s"There is no such variable ${expr.value}").pure[Alg] + ErroredPart(expr.name.value).pure[Alg] case (_, Some(t), _) => N.defineConstant(expr.name, t._2) as (ConstantRaw( expr.name.value, diff --git a/semantics/src/main/scala/aqua/semantics/expr/DataStructSem.scala b/semantics/src/main/scala/aqua/semantics/expr/DataStructSem.scala index 53563165..82349660 100644 --- a/semantics/src/main/scala/aqua/semantics/expr/DataStructSem.scala +++ b/semantics/src/main/scala/aqua/semantics/expr/DataStructSem.scala @@ -1,13 +1,12 @@ package aqua.semantics.expr import aqua.parser.expr.DataStructExpr -import aqua.raw.{Raw, TypeRaw} +import aqua.raw.{ErroredPart, Raw, TypeRaw} import aqua.semantics.Prog import aqua.semantics.rules.definitions.DefinitionsAlgebra import aqua.semantics.rules.names.NamesAlgebra import aqua.semantics.rules.types.TypesAlgebra import aqua.types.StructType - import cats.syntax.functor.* import cats.syntax.applicative.* import cats.syntax.traverse.* @@ -26,7 +25,7 @@ class DataStructSem[S[_]](val expr: DataStructExpr[S]) extends AnyVal { fields = defs.view.mapValues(d => d.name -> d.`type`).toMap structType <- T.defineStructType(expr.name, fields) result = structType.map(st => TypeRaw(expr.name.value, st)) - } yield result.getOrElse(Raw.error("Data struct types unresolved")) + } yield result.getOrElse(ErroredPart(expr.name.value)) ) } diff --git a/semantics/src/main/scala/aqua/semantics/expr/ServiceSem.scala b/semantics/src/main/scala/aqua/semantics/expr/ServiceSem.scala index c31d1b5d..aeae2f20 100644 --- a/semantics/src/main/scala/aqua/semantics/expr/ServiceSem.scala +++ b/semantics/src/main/scala/aqua/semantics/expr/ServiceSem.scala @@ -1,7 +1,8 @@ package aqua.semantics.expr +import aqua.helpers.syntax.optiont.withFilterF import aqua.parser.expr.ServiceExpr -import aqua.raw.{Raw, ServiceRaw} +import aqua.raw.{ErroredPart, Raw, ServiceRaw} import aqua.semantics.Prog import aqua.semantics.rules.ValuesAlgebra import aqua.semantics.rules.abilities.AbilitiesAlgebra @@ -9,16 +10,16 @@ import aqua.semantics.rules.definitions.DefinitionsAlgebra import aqua.semantics.rules.names.NamesAlgebra import aqua.semantics.rules.types.TypesAlgebra -import cats.data.EitherT -import cats.syntax.either.* -import cats.syntax.option.* -import cats.syntax.apply.* -import cats.syntax.flatMap.* -import cats.syntax.functor.* -import cats.syntax.traverse.* -import cats.syntax.foldable.* -import cats.syntax.applicative.* import cats.Monad +import cats.data.{EitherT, OptionT} +import cats.syntax.applicative.* +import cats.syntax.apply.* +import cats.syntax.either.* +import cats.syntax.flatMap.* +import cats.syntax.foldable.* +import cats.syntax.functor.* +import cats.syntax.option.* +import cats.syntax.traverse.* class ServiceSem[S[_]](val expr: ServiceExpr[S]) extends AnyVal { @@ -27,44 +28,30 @@ class ServiceSem[S[_]](val expr: ServiceExpr[S]) extends AnyVal { T: TypesAlgebra[S, Alg], V: ValuesAlgebra[S, Alg], D: DefinitionsAlgebra[S, Alg] - ): EitherT[Alg, Raw, ServiceRaw] = for { - arrows <- EitherT.fromOptionF( - // TODO: Move to purgeDefs here, allow not only arrows - // from parsing, throw errors here - D.purgeArrows(expr.name), - Raw.error("Service has no arrows") - ) - arrowsByName = arrows.map { case (name, arrow) => - name.value -> (name, arrow) - }.toNem - defaultId <- expr.id.traverse(id => - EitherT.fromOptionF( - V.valueToStringRaw(id), - Raw.error("Failed to resolve default service id") - ) - ) - serviceType <- EitherT.fromOptionF( - T.defineServiceType(expr.name, arrowsByName.toSortedMap), - Raw.error("Failed to define service type") - ) - arrowsDefs = arrows.map { case (name, _) => name.value -> name }.toNem - _ <- EitherT( - A.defineService( - expr.name, - arrowsDefs, + ): EitherT[Alg, Raw, ServiceRaw] = { + ( + for { + arrows <- OptionT(D.purgeArrows(expr.name)) + arrowsByName = arrows.map { case (name, arrow) => + name.value -> (name, arrow) + }.toNem + defaultId <- expr.id.traverse(id => OptionT(V.valueToStringRaw(id))) + serviceType <- OptionT(T.defineServiceType(expr.name, arrowsByName.toSortedMap)) + arrowsDefs = arrows.map { case (name, _) => name.value -> name }.toNem + _ <- OptionT.withFilterF( + A.defineService( + expr.name, + arrowsDefs, + defaultId + ) + ) + } yield ServiceRaw( + expr.name.value, + serviceType, defaultId - ).map(defined => - Raw - .error("Service not created due to validation errors") - .asLeft - .whenA(!defined) ) - ) - } yield ServiceRaw( - expr.name.value, - serviceType, - defaultId - ) + ).toRight(ErroredPart(expr.name.value)) + } def program[Alg[_]: Monad](using A: AbilitiesAlgebra[S, Alg], diff --git a/semantics/src/main/scala/aqua/semantics/expr/func/FuncSem.scala b/semantics/src/main/scala/aqua/semantics/expr/func/FuncSem.scala index 11c0962d..6fd49884 100644 --- a/semantics/src/main/scala/aqua/semantics/expr/func/FuncSem.scala +++ b/semantics/src/main/scala/aqua/semantics/expr/func/FuncSem.scala @@ -1,6 +1,6 @@ package aqua.semantics.expr.func -import aqua.raw.Raw +import aqua.raw.{ErroredPart, Raw} import aqua.raw.arrow.{ArrowRaw, FuncRaw} import aqua.parser.expr.FuncExpr import aqua.parser.lexer.Arg @@ -22,7 +22,7 @@ class FuncSem[S[_]](val expr: FuncExpr[S]) extends AnyVal { N.defineArrow(expr.name, arrow.`type`, isRoot = true) as FuncRaw(expr.name.value, arrow) case _ => - Raw.error("Func must continue with an arrow definition").pure[Alg] + ErroredPart(expr.name.value).pure[Alg] } }