diff --git a/semantics/src/main/scala/aqua/semantics/expr/CallArrowSem.scala b/semantics/src/main/scala/aqua/semantics/expr/CallArrowSem.scala index a76e4d6f..896e636d 100644 --- a/semantics/src/main/scala/aqua/semantics/expr/CallArrowSem.scala +++ b/semantics/src/main/scala/aqua/semantics/expr/CallArrowSem.scala @@ -9,12 +9,13 @@ import aqua.semantics.rules.ValuesAlgebra import aqua.semantics.rules.abilities.AbilitiesAlgebra import aqua.semantics.rules.names.NamesAlgebra import aqua.semantics.rules.types.TypesAlgebra -import aqua.types.{ArrowType, ScalarType, StreamType, Type} +import aqua.types.{ArrowType, StreamType, Type} import cats.Traverse import cats.free.Free import cats.syntax.apply._ import cats.syntax.flatMap._ import cats.syntax.functor._ +import cats.syntax.traverse._ class CallArrowSem[F[_]](val expr: CallArrowExpr[F]) extends AnyVal { @@ -57,39 +58,49 @@ class CallArrowSem[F[_]](val expr: CallArrowExpr[F]) extends AnyVal { case (Some(at), Some(sid)) => Option(at -> sid) // Here we assume that Ability is a Service that must be resolved case _ => None - }.flatMap(_.fold(Free.pure[Alg, Option[FuncOp]](None)) { case (arrowType, serviceId) => - checkArgsRes(arrowType).map { - case (argsResolved, t) => - Option( - FuncOp.leaf( - CallServiceTag( - serviceId = serviceId, - funcName = funcName.value, - Call(argsResolved, (variable.map(_.value), t).mapN(Call.Export)) - ) - ) - ) - - case _ => None - - } - }) + }.flatMap(_.map { case (arrowType, serviceId) => + callServiceTag(arrowType, Option(serviceId)) + }.traverse(identity)) case None => N.readArrow(funcName) - .flatMap(_.fold(Free.pure[Alg, Option[FuncOp]](None)) { arrowType => - checkArgsRes(arrowType).map { case (argsResolved, t) => - FuncOp.leaf( - CallArrowTag( - funcName = funcName.value, - Call(argsResolved, (variable.map(_.value), t).mapN(Call.Export)) - ) - ) - - } - .map(Option(_)) - }) + .flatMap(_.map { arrowType => + callServiceTag(arrowType, None) + }.traverse(identity)) } + def callServiceTag[Alg[_]](arrowType: ArrowType, serviceId: Option[ValueModel])(implicit + N: NamesAlgebra[F, Alg], + A: AbilitiesAlgebra[F, Alg], + T: TypesAlgebra[F, Alg], + V: ValuesAlgebra[F, Alg] + ): Free[Alg, FuncOp] = { + checkArgsRes(arrowType).flatMap { case (argsResolved, tOp) => + ((variable, tOp) match { + case (Some(v), Some(t)) => + Free.pure[Alg, Option[Call.Export]](Option(Call.Export(v.value, t))) + case (Some(v), None) => + T.expectNoExport(v).map(_ => None) + case _ => + Free.pure[Alg, Option[Call.Export]](None) + + }).map(call => + FuncOp.leaf(serviceId match { + case Some(sid) => + CallServiceTag( + serviceId = sid, + funcName = funcName.value, + Call(argsResolved, call) + ) + case None => + CallArrowTag( + funcName = funcName.value, + Call(argsResolved, call) + ) + }) + ) + } + } + def program[Alg[_]](implicit N: NamesAlgebra[F, Alg], A: AbilitiesAlgebra[F, Alg], diff --git a/semantics/src/main/scala/aqua/semantics/rules/types/TypeOp.scala b/semantics/src/main/scala/aqua/semantics/rules/types/TypeOp.scala index f4a165f3..0e3035fa 100644 --- a/semantics/src/main/scala/aqua/semantics/rules/types/TypeOp.scala +++ b/semantics/src/main/scala/aqua/semantics/rules/types/TypeOp.scala @@ -1,7 +1,7 @@ package aqua.semantics.rules.types import aqua.model.LambdaModel -import aqua.parser.lexer.{ArrowTypeToken, CustomTypeToken, LambdaOp, Name, Token, TypeToken} +import aqua.parser.lexer._ import aqua.types.{ArrowType, Type} import cats.data.NonEmptyMap @@ -23,5 +23,7 @@ case class ResolveLambda[F[_]](root: Type, ops: List[LambdaOp[F]]) case class EnsureTypeMatches[F[_]](token: Token[F], expected: Type, given: Type) extends TypeOp[F, Boolean] +case class ExpectNoExport[F[_]](token: Token[F]) extends TypeOp[F, Unit] + case class CheckArgumentsNum[F[_]](token: Token[F], expected: Int, given: Int) extends TypeOp[F, Boolean] diff --git a/semantics/src/main/scala/aqua/semantics/rules/types/TypesAlgebra.scala b/semantics/src/main/scala/aqua/semantics/rules/types/TypesAlgebra.scala index 5b19642c..68c85f02 100644 --- a/semantics/src/main/scala/aqua/semantics/rules/types/TypesAlgebra.scala +++ b/semantics/src/main/scala/aqua/semantics/rules/types/TypesAlgebra.scala @@ -1,7 +1,7 @@ package aqua.semantics.rules.types import aqua.model.LambdaModel -import aqua.parser.lexer.{ArrowTypeToken, CustomTypeToken, LambdaOp, Name, Token, TypeToken} +import aqua.parser.lexer._ import aqua.types.{ArrowType, Type} import cats.InjectK import cats.data.NonEmptyMap @@ -36,6 +36,9 @@ class TypesAlgebra[F[_], Alg[_]](implicit T: InjectK[TypeOp[F, *], Alg]) { def ensureTypeMatches(token: Token[F], expected: Type, given: Type): Free[Alg, Boolean] = Free.liftInject[Alg](EnsureTypeMatches[F](token, expected, given)) + def expectNoExport(token: Token[F]): Free[Alg, Unit] = + Free.liftInject[Alg](ExpectNoExport[F](token)) + def checkArgumentsNumber(token: Token[F], expected: Int, given: Int): Free[Alg, Boolean] = Free.liftInject[Alg](CheckArgumentsNum(token, expected, given)) } diff --git a/semantics/src/main/scala/aqua/semantics/rules/types/TypesInterpreter.scala b/semantics/src/main/scala/aqua/semantics/rules/types/TypesInterpreter.scala index 9994b833..be3c93ae 100644 --- a/semantics/src/main/scala/aqua/semantics/rules/types/TypesInterpreter.scala +++ b/semantics/src/main/scala/aqua/semantics/rules/types/TypesInterpreter.scala @@ -1,15 +1,14 @@ package aqua.semantics.rules.types -import aqua.semantics.rules.ReportError import aqua.parser.lexer.Token - +import aqua.semantics.rules.ReportError import aqua.types.{ArrowType, ProductType} import cats.data.Validated.{Invalid, Valid} import cats.data.{NonEmptyMap, State} +import cats.syntax.flatMap._ +import cats.syntax.functor._ import cats.~> import monocle.Lens -import cats.syntax.functor._ -import cats.syntax.flatMap._ import scala.collection.immutable.SortedMap @@ -105,6 +104,12 @@ class TypesInterpreter[F[_], X](implicit lens: Lens[X, TypesState[F]], error: Re report(etm.token, s"Types mismatch, expected: ${etm.expected}, given: ${etm.`given`}") .as(false) + case ene: ExpectNoExport[F] => + report( + ene.token, + "Types mismatch. Cannot assign to a variable the result of a call that returns nothing" + ).as(()) + case ca: CheckArgumentsNum[F] => if (ca.expected == ca.given) State.pure(true) else