fix(compiler): Return error if SemanticError occures [LNG-356] (#1126)

This commit is contained in:
Dima 2024-04-17 15:40:19 +03:00 committed by GitHub
parent f29e44e52a
commit e6c5d0039f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 216 additions and 107 deletions

View File

@ -1,10 +1,29 @@
aqua Main aqua Job declares *
export main export aaa, bbb
func reward(amount: ?u32) -> u32: data Peer:
result = ?[amount! / 10, 0] id: string
<- result!
func main(a: u32) -> u32: func aaa() -> Peer:
<- reward(?[a]) 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

View File

@ -33,9 +33,9 @@ object LspContext {
def blank[S[_]]: LspContext[S] = LspContext[S](raw = RawContext()) def blank[S[_]]: LspContext[S] = LspContext[S](raw = RawContext())
given [S[_]]: Monoid[LspContext[S]] with { 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]( LspContext[S](
raw = x.raw |+| y.raw, raw = x.raw |+| y.raw,
abDefinitions = x.abDefinitions ++ y.abDefinitions, abDefinitions = x.abDefinitions ++ y.abDefinitions,
@ -52,7 +52,7 @@ object LspContext {
given [S[_]]: Picker[LspContext[S]] with { given [S[_]]: Picker[LspContext[S]] with {
import aqua.semantics.header.Picker.* 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 exports(ctx: LspContext[S]): Map[String, Option[String]] = ctx.raw.exports
override def isAbility(ctx: LspContext[S], name: String): Boolean = override def isAbility(ctx: LspContext[S], name: String): Boolean =

View File

@ -2,21 +2,15 @@ package aqua.lsp
import aqua.parser.Ast import aqua.parser.Ast
import aqua.parser.head.{ImportExpr, ImportFromExpr, UseExpr, UseFromExpr} 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.raw.ConstantRaw
import aqua.semantics.*
import aqua.semantics.header.Picker.* import aqua.semantics.header.Picker.*
import aqua.semantics.rules.locations.LocationsState import aqua.semantics.rules.locations.LocationsState
import aqua.semantics.{CompilerState, RawSemantics, SemanticError, SemanticWarning, Semantics}
import cats.data.Validated.{Invalid, Valid} import cats.data.{EitherT, NonEmptyChain, Writer}
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.syntax.functor.* import cats.syntax.functor.*
import cats.syntax.reducible.* import cats.syntax.applicative.*
import monocle.Lens import monocle.Lens
import monocle.macros.GenLens import monocle.macros.GenLens
@ -74,7 +68,6 @@ class LspSemantics[S[_]](
warnings = state.warnings.toList warnings = state.warnings.toList
).pure[Result] ).pure[Result]
} }
// TODO: return as Eval
.value .value
} }
} }

View File

@ -3,10 +3,12 @@ package aqua.lsp
import aqua.compiler.FileIdString.given_FileId_String import aqua.compiler.FileIdString.given_FileId_String
import aqua.compiler.{AquaCompilerConf, AquaError, AquaSources} import aqua.compiler.{AquaCompilerConf, AquaError, AquaSources}
import aqua.parser.Parser import aqua.parser.Parser
import aqua.parser.lexer.Token
import aqua.parser.lift.Span import aqua.parser.lift.Span
import aqua.parser.lift.Span.S import aqua.parser.lift.Span.S
import aqua.raw.ConstantRaw import aqua.raw.ConstantRaw
import aqua.semantics.rules.locations.{DefinitionInfo, TokenLocation, VariableInfo} import aqua.semantics.rules.locations.{DefinitionInfo, TokenLocation, VariableInfo}
import aqua.semantics.{RulesViolated, SemanticError}
import aqua.types.* import aqua.types.*
import cats.Id import cats.Id
@ -131,6 +133,16 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside {
ee 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 { it should "return right tokens" in {
val main = val main =
"""aqua Import declares foo_wrapper, Ab, Str, useAbAndStruct, SOME_CONST """aqua Import declares foo_wrapper, Ab, Str, useAbAndStruct, SOME_CONST
@ -153,6 +165,7 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside {
| num(someVar) | num(someVar)
| OneMore fooResult | OneMore fooResult
| OneMore.more_call() | OneMore.more_call()
| <- "123"
| |
|ability Ab: |ability Ab:
| someField: u32 | someField: u32
@ -362,26 +375,26 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside {
| a: SomeAlias | a: SomeAlias
| |
|data SomeStruct: |data SomeStruct:
| al: SomeAlias | al11: SomeAlias
| nested: NestedStruct | nested: NestedStruct
| |
|ability SomeAbility: |ability SomeAbility:
| someStr: SomeStruct | someStr: SomeStruct
| nested: NestedStruct | nested: NestedStruct
| al: SomeAlias | al11: SomeAlias
| someFunc(ss: SomeStruct, nest: NestedStruct, al: SomeAlias) -> NestedStruct, SomeStruct, SomeAlias | someFunc(ss: SomeStruct, nest: NestedStruct, al11: SomeAlias) -> NestedStruct, SomeStruct, SomeAlias
| |
|service Srv("a"): |service Srv("a"):
| check(ss: SomeStruct, nest: NestedStruct, al: SomeAlias) -> NestedStruct | check(ss: SomeStruct, nest: NestedStruct, al11: SomeAlias) -> NestedStruct
| check2() -> SomeStruct | check2() -> SomeStruct
| check3() -> SomeAlias | check3() -> SomeAlias
| |
|func withAb{SomeAbility}() -> SomeStruct: |func withAb{SomeAbility}() -> SomeStruct:
| Srv.check() | Srv.check(SomeAbility.someStr, SomeAbility.nested, SomeAbility.al11)
| Srv.check2() | Srv.check2()
| <- SomeAbility.someStr | <- SomeAbility.someStr
| |
|func main(ss: SomeStruct, nest: NestedStruct, al: SomeAlias) -> string: |func main(ss: SomeStruct, nest: NestedStruct, al11: SomeAlias) -> string:
| Srv.check3() | Srv.check3()
| <- "" | <- ""
|""".stripMargin |""".stripMargin
@ -394,11 +407,11 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside {
val nestedType = StructType("NestedStruct", NonEmptyMap.of(("a", ScalarType.string))) val nestedType = StructType("NestedStruct", NonEmptyMap.of(("a", ScalarType.string)))
val someStr = val someStr =
StructType("SomeStruct", NonEmptyMap.of(("nested", nestedType), ("al", ScalarType.string))) StructType("SomeStruct", NonEmptyMap.of(("nested", nestedType), ("al11", ScalarType.string)))
val abFuncType = ArrowType( val abFuncType = ArrowType(
ProductType.labelled( ProductType.labelled(
("ss", someStr) :: ("nest", nestedType) :: ("al", ScalarType.string) :: Nil ("ss", someStr) :: ("nest", nestedType) :: ("al11", ScalarType.string) :: Nil
), ),
ProductType(nestedType :: someStr :: ScalarType.string :: Nil) ProductType(nestedType :: someStr :: ScalarType.string :: Nil)
) )
@ -407,7 +420,7 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside {
NonEmptyMap.of( NonEmptyMap.of(
("someStr", someStr), ("someStr", someStr),
("nested", nestedType), ("nested", nestedType),
("al", ScalarType.string), ("al11", ScalarType.string),
("someFunc", abFuncType) ("someFunc", abFuncType)
) )
) )
@ -419,7 +432,7 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside {
"check", "check",
ArrowType( ArrowType(
ProductType.labelled( ProductType.labelled(
("ss", someStr) :: ("nest", nestedType) :: ("al", ScalarType.string) :: Nil ("ss", someStr) :: ("nest", nestedType) :: ("al11", ScalarType.string) :: Nil
), ),
ProductType(nestedType :: Nil) ProductType(nestedType :: Nil)
) )
@ -445,10 +458,14 @@ class AquaLSPSpec extends AnyFlatSpec with Matchers with Inside {
} }
res.checkTokenLoc(main, "SomeAbility", 0, someAb) shouldBe true res.checkTokenLoc(main, "SomeAbility", 0, someAb) shouldBe true
// from {SomeAbility} to 'ability SomeAbility' Range.inclusive(1, 5).foreach { n =>
res.checkLocations("SomeAbility", 0, 1, main) shouldBe true res.checkLocations("SomeAbility", 0, n, main) shouldBe true
// from 'SomeAbility.someStr' to {SomeAbility} }
res.checkLocations("SomeAbility", 0, 2, 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 res.checkTokenLoc(main, "Srv", 0, srvType) shouldBe true
Range.inclusive(1, 3).foreach { n => 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, 2, main) shouldBe true
res.checkLocations("Abilyy", 0, 3, 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)
}
} }

View File

@ -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)
}

View File

@ -90,7 +90,7 @@ case class RawContext(
parts.map { case (_, p) => p.name }.toList.toSet parts.map { case (_, p) => p.name }.toList.toSet
lazy val declaredNames: Set[String] = lazy val declaredNames: Set[String] =
allNames.filter(declares.contains) allNames.intersect(declares)
override def toString: String = override def toString: String =
s"""|module: ${module.getOrElse("unnamed")} s"""|module: ${module.getOrElse("unnamed")}
@ -109,7 +109,7 @@ object RawContext {
override def empty: RawContext = blank override def empty: RawContext = blank
override def combine(x: RawContext, y: RawContext) = override def combine(x: RawContext, y: RawContext): RawContext =
RawContext( RawContext(
x.module orElse y.module, x.module orElse y.module,
x.declares ++ y.declares, x.declares ++ y.declares,

View File

@ -56,7 +56,6 @@ class RawSemantics[S[_]](
) )
) )
} }
// TODO: return as Eval
.value .value
} }
} }

View File

@ -1,24 +1,15 @@
package aqua.semantics.expr package aqua.semantics.expr
import aqua.parser.expr.AbilityExpr import aqua.parser.expr.AbilityExpr
import aqua.raw.{Raw, TypeRaw} import aqua.raw.{ErroredPart, Raw, TypeRaw}
import aqua.parser.lexer.{Name, NamedTypeToken}
import aqua.semantics.Prog 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.definitions.DefinitionsAlgebra
import aqua.semantics.rules.names.NamesAlgebra
import aqua.semantics.rules.types.TypesAlgebra import aqua.semantics.rules.types.TypesAlgebra
import aqua.types.{AbilityType, ArrowType, Type}
import cats.Monad
import cats.syntax.apply.* import cats.syntax.apply.*
import cats.syntax.flatMap.* import cats.syntax.flatMap.*
import cats.syntax.functor.* 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 { 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 fields = defs.view.mapValues(d => d.name -> d.`type`).toMap
abilityType <- T.defineAbilityType(expr.name, fields) abilityType <- T.defineAbilityType(expr.name, fields)
result = abilityType.map(st => TypeRaw(expr.name.value, st)) result = abilityType.map(st => TypeRaw(expr.name.value, st))
} yield result.getOrElse(Raw.error("Ability types unresolved")) } yield result.getOrElse(ErroredPart(expr.name.value))
) )
} }
} }

View File

@ -1,10 +1,9 @@
package aqua.semantics.expr package aqua.semantics.expr
import aqua.parser.expr.AliasExpr import aqua.parser.expr.AliasExpr
import aqua.raw.{Raw, TypeRaw} import aqua.raw.{ErroredPart, Raw, TypeRaw}
import aqua.semantics.Prog import aqua.semantics.Prog
import aqua.semantics.rules.types.TypesAlgebra import aqua.semantics.rules.types.TypesAlgebra
import cats.Applicative import cats.Applicative
import cats.Monad import cats.Monad
import cats.syntax.flatMap.* 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] = def program[Alg[_]: Monad](using T: TypesAlgebra[S, Alg]): Prog[Alg, Raw] =
T.resolveType(expr.target).flatMap { T.resolveType(expr.target).flatMap {
case Some(t) => T.defineAlias(expr.name, t) as (TypeRaw(expr.name.value, t): Raw) 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))
} }
} }

View File

@ -1,7 +1,7 @@
package aqua.semantics.expr package aqua.semantics.expr
import aqua.parser.expr.ConstantExpr import aqua.parser.expr.ConstantExpr
import aqua.raw.{ConstantRaw, Raw} import aqua.raw.{ConstantRaw, ErroredPart, Raw}
import aqua.semantics.Prog import aqua.semantics.Prog
import aqua.semantics.rules.ValuesAlgebra import aqua.semantics.rules.ValuesAlgebra
import aqua.semantics.rules.names.NamesAlgebra import aqua.semantics.rules.names.NamesAlgebra
@ -27,12 +27,12 @@ class ConstantSem[S[_]](val expr: ConstantExpr[S]) extends AnyVal {
case true => case true =>
Raw.empty(s"Constant with name ${expr.name} was already defined, skipping") Raw.empty(s"Constant with name ${expr.name} was already defined, skipping")
case false => case false =>
Raw.error(s"Constant with name ${expr.name} was defined with different type") ErroredPart(expr.name.value)
} }
case (Some(_), _, _) => case (Some(_), _, _) =>
Raw.error(s"Name '${expr.name.value}' was already defined").pure[Alg] Raw.error(s"Name '${expr.name.value}' was already defined").pure[Alg]
case (_, None, _) => case (_, None, _) =>
Raw.error(s"There is no such variable ${expr.value}").pure[Alg] ErroredPart(expr.name.value).pure[Alg]
case (_, Some(t), _) => case (_, Some(t), _) =>
N.defineConstant(expr.name, t._2) as (ConstantRaw( N.defineConstant(expr.name, t._2) as (ConstantRaw(
expr.name.value, expr.name.value,

View File

@ -1,13 +1,12 @@
package aqua.semantics.expr package aqua.semantics.expr
import aqua.parser.expr.DataStructExpr import aqua.parser.expr.DataStructExpr
import aqua.raw.{Raw, TypeRaw} import aqua.raw.{ErroredPart, Raw, TypeRaw}
import aqua.semantics.Prog import aqua.semantics.Prog
import aqua.semantics.rules.definitions.DefinitionsAlgebra import aqua.semantics.rules.definitions.DefinitionsAlgebra
import aqua.semantics.rules.names.NamesAlgebra import aqua.semantics.rules.names.NamesAlgebra
import aqua.semantics.rules.types.TypesAlgebra import aqua.semantics.rules.types.TypesAlgebra
import aqua.types.StructType import aqua.types.StructType
import cats.syntax.functor.* import cats.syntax.functor.*
import cats.syntax.applicative.* import cats.syntax.applicative.*
import cats.syntax.traverse.* 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 fields = defs.view.mapValues(d => d.name -> d.`type`).toMap
structType <- T.defineStructType(expr.name, fields) structType <- T.defineStructType(expr.name, fields)
result = structType.map(st => TypeRaw(expr.name.value, st)) 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))
) )
} }

View File

@ -1,7 +1,8 @@
package aqua.semantics.expr package aqua.semantics.expr
import aqua.helpers.syntax.optiont.withFilterF
import aqua.parser.expr.ServiceExpr import aqua.parser.expr.ServiceExpr
import aqua.raw.{Raw, ServiceRaw} import aqua.raw.{ErroredPart, Raw, ServiceRaw}
import aqua.semantics.Prog import aqua.semantics.Prog
import aqua.semantics.rules.ValuesAlgebra import aqua.semantics.rules.ValuesAlgebra
import aqua.semantics.rules.abilities.AbilitiesAlgebra 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.names.NamesAlgebra
import aqua.semantics.rules.types.TypesAlgebra 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.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 { 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], T: TypesAlgebra[S, Alg],
V: ValuesAlgebra[S, Alg], V: ValuesAlgebra[S, Alg],
D: DefinitionsAlgebra[S, Alg] D: DefinitionsAlgebra[S, Alg]
): EitherT[Alg, Raw, ServiceRaw] = for { ): EitherT[Alg, Raw, ServiceRaw] = {
arrows <- EitherT.fromOptionF( (
// TODO: Move to purgeDefs here, allow not only arrows for {
// from parsing, throw errors here arrows <- OptionT(D.purgeArrows(expr.name))
D.purgeArrows(expr.name), arrowsByName = arrows.map { case (name, arrow) =>
Raw.error("Service has no arrows") name.value -> (name, arrow)
) }.toNem
arrowsByName = arrows.map { case (name, arrow) => defaultId <- expr.id.traverse(id => OptionT(V.valueToStringRaw(id)))
name.value -> (name, arrow) serviceType <- OptionT(T.defineServiceType(expr.name, arrowsByName.toSortedMap))
}.toNem arrowsDefs = arrows.map { case (name, _) => name.value -> name }.toNem
defaultId <- expr.id.traverse(id => _ <- OptionT.withFilterF(
EitherT.fromOptionF( A.defineService(
V.valueToStringRaw(id), expr.name,
Raw.error("Failed to resolve default service id") arrowsDefs,
) defaultId
) )
serviceType <- EitherT.fromOptionF( )
T.defineServiceType(expr.name, arrowsByName.toSortedMap), } yield ServiceRaw(
Raw.error("Failed to define service type") expr.name.value,
) serviceType,
arrowsDefs = arrows.map { case (name, _) => name.value -> name }.toNem
_ <- EitherT(
A.defineService(
expr.name,
arrowsDefs,
defaultId defaultId
).map(defined =>
Raw
.error("Service not created due to validation errors")
.asLeft
.whenA(!defined)
) )
) ).toRight(ErroredPart(expr.name.value))
} yield ServiceRaw( }
expr.name.value,
serviceType,
defaultId
)
def program[Alg[_]: Monad](using def program[Alg[_]: Monad](using
A: AbilitiesAlgebra[S, Alg], A: AbilitiesAlgebra[S, Alg],

View File

@ -1,6 +1,6 @@
package aqua.semantics.expr.func package aqua.semantics.expr.func
import aqua.raw.Raw import aqua.raw.{ErroredPart, Raw}
import aqua.raw.arrow.{ArrowRaw, FuncRaw} import aqua.raw.arrow.{ArrowRaw, FuncRaw}
import aqua.parser.expr.FuncExpr import aqua.parser.expr.FuncExpr
import aqua.parser.lexer.Arg 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) N.defineArrow(expr.name, arrow.`type`, isRoot = true) as FuncRaw(expr.name.value, arrow)
case _ => case _ =>
Raw.error("Func must continue with an arrow definition").pure[Alg] ErroredPart(expr.name.value).pure[Alg]
} }
} }