From 69a808e24307b5fe312a6dfdc6041c310c33d96d Mon Sep 17 00:00:00 2001 From: InversionSpaces Date: Fri, 8 Sep 2023 14:21:19 +0200 Subject: [PATCH] fix(compiler): Error on not arrow call after `<-` (#876) * Add error report * Add tests --- .../semantics/expr/func/CallArrowSem.scala | 7 +--- .../aqua/semantics/rules/ValuesAlgebra.scala | 18 +++++++-- .../rules/errors/ErrorsAlgebra.scala | 3 ++ .../scala/aqua/semantics/SemanticsSpec.scala | 39 +++++++++++++++++++ 4 files changed, 59 insertions(+), 8 deletions(-) diff --git a/semantics/src/main/scala/aqua/semantics/expr/func/CallArrowSem.scala b/semantics/src/main/scala/aqua/semantics/expr/func/CallArrowSem.scala index ab825c6c..14b24a3f 100644 --- a/semantics/src/main/scala/aqua/semantics/expr/func/CallArrowSem.scala +++ b/semantics/src/main/scala/aqua/semantics/expr/func/CallArrowSem.scala @@ -40,11 +40,8 @@ class CallArrowSem[S[_]](val expr: CallArrowExpr[S]) extends AnyVal { T: TypesAlgebra[S, Alg], V: ValuesAlgebra[S, Alg] ): Alg[Option[FuncOp]] = for { - callArrowRaw <- V.valueToRaw(callArrow).map { - // TODO: Refactor this to support other results - case Some(car: CallArrowRaw) => car.some - case _ => none - } + // TODO: Accept other expressions + callArrowRaw <- V.valueToCallArrowRaw(expr.callArrow) maybeOp <- callArrowRaw.traverse(car => variables .drop(car.baseType.codomain.length) diff --git a/semantics/src/main/scala/aqua/semantics/rules/ValuesAlgebra.scala b/semantics/src/main/scala/aqua/semantics/rules/ValuesAlgebra.scala index 171df5cd..c5acf483 100644 --- a/semantics/src/main/scala/aqua/semantics/rules/ValuesAlgebra.scala +++ b/semantics/src/main/scala/aqua/semantics/rules/ValuesAlgebra.scala @@ -7,6 +7,7 @@ import aqua.raw.value.* import aqua.semantics.rules.abilities.AbilitiesAlgebra import aqua.semantics.rules.names.NamesAlgebra import aqua.semantics.rules.types.TypesAlgebra +import aqua.semantics.rules.errors.ErrorsAlgebra import aqua.types.* import cats.Monad @@ -25,9 +26,10 @@ import scribe.Logging import scala.collection.immutable.SortedMap -class ValuesAlgebra[S[_], Alg[_]: Monad](implicit +class ValuesAlgebra[S[_], Alg[_]: Monad](using N: NamesAlgebra[S, Alg], T: TypesAlgebra[S, Alg], + E: ErrorsAlgebra[S, Alg], A: AbilitiesAlgebra[S, Alg] ) extends Logging { @@ -310,6 +312,15 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](implicit } } + def valueToCallArrowRaw(v: ValueToken[S]): Alg[Option[CallArrowRaw]] = + valueToRaw(v).flatMap( + _.flatTraverse { + case ca: CallArrowRaw => ca.some.pure[Alg] + // TODO: better error message (`raw` formatting) + case raw => E.report(v, s"Expected arrow call, got $raw").as(none) + } + ) + private def callArrowFromAbility( ab: Name[S], at: AbilityType, @@ -402,10 +413,11 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](implicit object ValuesAlgebra { - implicit def deriveValuesAlgebra[S[_], Alg[_]: Monad](implicit + given [S[_], Alg[_]: Monad](using N: NamesAlgebra[S, Alg], T: TypesAlgebra[S, Alg], - A: AbilitiesAlgebra[S, Alg] + A: AbilitiesAlgebra[S, Alg], + E: ErrorsAlgebra[S, Alg] ): ValuesAlgebra[S, Alg] = new ValuesAlgebra[S, Alg] } diff --git a/semantics/src/main/scala/aqua/semantics/rules/errors/ErrorsAlgebra.scala b/semantics/src/main/scala/aqua/semantics/rules/errors/ErrorsAlgebra.scala index adef5a8f..ddd5dfc7 100644 --- a/semantics/src/main/scala/aqua/semantics/rules/errors/ErrorsAlgebra.scala +++ b/semantics/src/main/scala/aqua/semantics/rules/errors/ErrorsAlgebra.scala @@ -4,4 +4,7 @@ import aqua.parser.lexer.Token trait ErrorsAlgebra[S[_], Alg[_]] { def report(token: Token[S], hints: List[String]): Alg[Unit] + + def report(token: Token[S], hint: String): Alg[Unit] = + report(token, hint :: Nil) } diff --git a/semantics/src/test/scala/aqua/semantics/SemanticsSpec.scala b/semantics/src/test/scala/aqua/semantics/SemanticsSpec.scala index 2b232d5f..49ddcd5d 100644 --- a/semantics/src/test/scala/aqua/semantics/SemanticsSpec.scala +++ b/semantics/src/test/scala/aqua/semantics/SemanticsSpec.scala @@ -611,4 +611,43 @@ class SemanticsSpec extends AnyFlatSpec with Matchers with Inside { atLeast(1, errors.toChain.toList) shouldBe a[RulesViolated[Span.S]] } } + + it should "forbid not arrow calls after <-" in { + def scriptPush(prefix: String, what: String) = + s""" + |func main() -> []string: + | stream: *string + |${prefix.split("\n").map(" " + _).mkString("\n")} + | stream <- $what + | <- stream + |""".stripMargin + + val scriptLiteral = scriptPush("", "\"a\"") + + insideSemErrors(scriptLiteral) { errors => + atLeast(1, errors.toChain.toList) shouldBe a[RulesViolated[Span.S]] + } + + val scriptVar = scriptPush( + """ + |variable = "value" + |""".stripMargin, + "variable" + ) + + insideSemErrors(scriptVar) { errors => + atLeast(1, errors.toChain.toList) shouldBe a[RulesViolated[Span.S]] + } + + val scriptArrayElement = scriptPush( + """ + |arr = ["a", "b", "c"] + |""".stripMargin, + "arr[0]" + ) + + insideSemErrors(scriptArrayElement) { errors => + atLeast(1, errors.toChain.toList) shouldBe a[RulesViolated[Span.S]] + } + } }