Added bang op to get array items by index (#144)

This commit is contained in:
Dmitry Kurinskiy 2021-06-01 20:07:09 +03:00 committed by GitHub
parent 6ba9c13c63
commit 378d154ff7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 191 additions and 138 deletions

View File

@ -50,7 +50,9 @@ jobs:
- name: Integration test - name: Integration test
run: | run: |
git clone https://github.com/fluencelabs/aqua-playground.git git clone https://github.com/fluencelabs/aqua-playground.git
sbt "cli/run -i aqua-playground/aqua/examples -o aqua-playground/src/compiled/examples"
cd aqua-playground cd aqua-playground
npm i npm i
cd ..
sbt "cli/run -i aqua-playground/aqua/examples -o aqua-playground/src/compiled/examples -m aqua-playground/node_modules"
cd aqua-playground
npm run examples npm run examples

View File

@ -13,15 +13,19 @@ service Test("test"):
getUserList: -> []User getUserList: -> []User
doSomething: -> bool doSomething: -> bool
func betterMessage(relay: string): func betterMessage(relay: string, arr: []string) -> string:
on relay: on relay:
Peer.is_connected("something") Peer.is_connected("something")
par isOnline <- Peer.is_connected(relay) par isOnline <- Peer.is_connected(relay)
par on "quray": par on "quray":
Peer.is_connected("qurara") Peer.is_connected("qurara")
stream: *string
if isOnline: if isOnline:
try: try:
Test.doSomething() Test.doSomething()
else: else:
Peer.is_connected("else") Peer.is_connected(stream!)
<- stream!2

View File

@ -17,14 +17,16 @@ object AirGen {
def lambdaToString(ls: List[LambdaModel]): String = ls match { def lambdaToString(ls: List[LambdaModel]): String = ls match {
case Nil => "" case Nil => ""
case IntoArrayModel :: tail => case IntoArrayModel(_) :: tail =>
s"[@${lambdaToString(tail)}]" s"[@${lambdaToString(tail)}]"
case IntoFieldModel(field) :: tail => case IntoFieldModel(field, _) :: tail =>
s".$field${lambdaToString(tail)}" s".$field${lambdaToString(tail)}"
case IntoIndexModel(idx, _) :: tail =>
s".[$idx]${lambdaToString(tail)}"
} }
def valueToData(vm: ValueModel): DataView = vm match { def valueToData(vm: ValueModel): DataView = vm match {
case LiteralModel(value) => DataView.StringScalar(value) case LiteralModel(value, _) => DataView.StringScalar(value)
case VarModel(name, t, lambda) => case VarModel(name, t, lambda) =>
val n = t match { val n = t match {
case _: StreamType => "$" + name case _: StreamType => "$" + name

View File

@ -22,7 +22,7 @@ val declineEnumV = "1.3.0"
name := "aqua-hll" name := "aqua-hll"
val commons = Seq( val commons = Seq(
baseAquaVersion := "0.1.2", baseAquaVersion := "0.1.3",
version := baseAquaVersion.value + "-" + sys.env.getOrElse("BUILD_NUMBER", "SNAPSHOT"), version := baseAquaVersion.value + "-" + sys.env.getOrElse("BUILD_NUMBER", "SNAPSHOT"),
scalaVersion := dottyVersion, scalaVersion := dottyVersion,
libraryDependencies ++= Seq( libraryDependencies ++= Seq(

View File

@ -1,10 +1,14 @@
package aqua.model package aqua.model
import aqua.types.Type import aqua.types.{ScalarType, Type}
import cats.Eq import cats.Eq
import cats.data.Chain import cats.data.Chain
sealed trait ValueModel { sealed trait ValueModel {
def `type`: Type
def lastType: Type
def resolveWith(map: Map[String, ValueModel]): ValueModel = this def resolveWith(map: Map[String, ValueModel]): ValueModel = this
} }
@ -15,20 +19,29 @@ object ValueModel {
} }
} }
case class LiteralModel(value: String) extends ValueModel case class LiteralModel(value: String, `type`: Type) extends ValueModel {
override def lastType: Type = `type`
object LiteralModel {
val initPeerId: LiteralModel = LiteralModel("%init_peer_id%")
} }
sealed trait LambdaModel object LiteralModel {
case object IntoArrayModel extends LambdaModel def quote(str: String): LiteralModel = LiteralModel("\"" + str + "\"", ScalarType.string)
case class IntoFieldModel(field: String) extends LambdaModel
val initPeerId: LiteralModel = LiteralModel("%init_peer_id%", ScalarType.string)
}
sealed trait LambdaModel {
def `type`: Type
}
case class IntoArrayModel(`type`: Type) extends LambdaModel
case class IntoFieldModel(field: String, `type`: Type) extends LambdaModel
case class IntoIndexModel(idx: Int, `type`: Type) extends LambdaModel
case class VarModel(name: String, `type`: Type, lambda: Chain[LambdaModel] = Chain.empty) case class VarModel(name: String, `type`: Type, lambda: Chain[LambdaModel] = Chain.empty)
extends ValueModel { extends ValueModel {
def deriveFrom(vm: VarModel): VarModel = vm.copy(lambda = vm.lambda ++ lambda) def deriveFrom(vm: VarModel): VarModel = vm.copy(lambda = vm.lambda ++ lambda)
override val lastType: Type = lambda.lastOption.map(_.`type`).getOrElse(`type`)
override def resolveWith(map: Map[String, ValueModel]): ValueModel = { override def resolveWith(map: Map[String, ValueModel]): ValueModel = {
map.get(name) match { map.get(name) match {
case Some(vv: VarModel) => case Some(vv: VarModel) =>

View File

@ -8,7 +8,7 @@ import cats.free.Cofree
object FuncOps { object FuncOps {
def noop(peerId: ValueModel): FuncOp = def noop(peerId: ValueModel): FuncOp =
FuncOp.leaf(CallServiceTag(LiteralModel("\"op\""), "identity", Call(Nil, None), Some(peerId))) FuncOp.leaf(CallServiceTag(LiteralModel.quote("op"), "identity", Call(Nil, None), Some(peerId)))
def callService(srvId: ValueModel, funcName: String, call: Call): FuncOp = def callService(srvId: ValueModel, funcName: String, call: Call): FuncOp =
FuncOp.leaf( FuncOp.leaf(

View File

@ -12,9 +12,9 @@ case class BodyConfig(
wrapWithXor: Boolean = true wrapWithXor: Boolean = true
) { ) {
val errorId: ValueModel = LiteralModel("\"" + errorFuncName + "\"") val errorId: ValueModel = LiteralModel.quote(errorFuncName)
val errorHandlingCallback: ValueModel = LiteralModel("\"" + errorHandlingService + "\"") val errorHandlingCallback: ValueModel = LiteralModel.quote(errorHandlingService)
val callbackSrvId: ValueModel = LiteralModel("\"" + callbackService + "\"") val callbackSrvId: ValueModel = LiteralModel.quote(callbackService)
val dataSrvId: ValueModel = LiteralModel("\"" + getDataService + "\"") val dataSrvId: ValueModel = LiteralModel.quote(getDataService)
} }

View File

@ -3,6 +3,7 @@ package aqua.model.transform
import aqua.model.{LiteralModel, ValueModel} import aqua.model.{LiteralModel, ValueModel}
import aqua.model.func.Call import aqua.model.func.Call
import aqua.model.func.body.{FuncOp, FuncOps, MatchMismatchTag, OnTag, OpTag, XorTag} import aqua.model.func.body.{FuncOp, FuncOps, MatchMismatchTag, OnTag, OpTag, XorTag}
import aqua.types.LiteralType
import cats.Eval import cats.Eval
import cats.data.Chain import cats.data.Chain
import cats.free.Cofree import cats.free.Cofree
@ -53,10 +54,10 @@ case class ErrorsCatcher(
object ErrorsCatcher { object ErrorsCatcher {
// TODO not a string // TODO not a string
val lastErrorArg: ValueModel = LiteralModel("%last_error%") val lastErrorArg: ValueModel = LiteralModel("%last_error%", LiteralType.string)
def lastErrorCall(i: Int): Call = Call( def lastErrorCall(i: Int): Call = Call(
lastErrorArg :: LiteralModel(i.toString) :: Nil, lastErrorArg :: LiteralModel(i.toString, LiteralType.number) :: Nil,
None None
) )
} }

View File

@ -3,7 +3,7 @@ package aqua.model
import aqua.model.func.Call import aqua.model.func.Call
import aqua.model.func.body._ import aqua.model.func.body._
import aqua.model.transform.BodyConfig import aqua.model.transform.BodyConfig
import aqua.types.ScalarType import aqua.types.{LiteralType, ScalarType}
import cats.Eval import cats.Eval
import cats.data.Chain import cats.data.Chain
import cats.free.Cofree import cats.free.Cofree
@ -84,24 +84,30 @@ object Node {
implicit def nodeToCof(tree: Node): Cof = implicit def nodeToCof(tree: Node): Cof =
Cofree(tree.tag, Eval.later(Chain.fromSeq(tree.ops.map(nodeToCof)))) Cofree(tree.tag, Eval.later(Chain.fromSeq(tree.ops.map(nodeToCof))))
val relay = LiteralModel("-relay-") val relay = LiteralModel("-relay-", ScalarType.string)
val relayV = VarModel("-relay-", ScalarType.string) val relayV = VarModel("-relay-", ScalarType.string)
val initPeer = LiteralModel.initPeerId val initPeer = LiteralModel.initPeerId
val emptyCall = Call(Nil, None) val emptyCall = Call(Nil, None)
val otherPeer = LiteralModel("other-peer") val otherPeer = LiteralModel("other-peer", ScalarType.string)
val otherRelay = LiteralModel("other-relay") val otherRelay = LiteralModel("other-relay", ScalarType.string)
val otherPeer2 = LiteralModel("other-peer-2") val otherPeer2 = LiteralModel("other-peer-2", ScalarType.string)
val otherRelay2 = LiteralModel("other-relay-2") val otherRelay2 = LiteralModel("other-relay-2", ScalarType.string)
def call(i: Int, on: ValueModel = null) = Node( def call(i: Int, on: ValueModel = null) = Node(
CallServiceTag(LiteralModel(s"srv$i"), s"fn$i", Call(Nil, None), Option(on)) CallServiceTag(LiteralModel(s"srv$i", ScalarType.string), s"fn$i", Call(Nil, None), Option(on))
) )
def errorCall(bc: BodyConfig, i: Int, on: ValueModel = null) = Node( def errorCall(bc: BodyConfig, i: Int, on: ValueModel = null) = Node(
CallServiceTag( CallServiceTag(
bc.errorHandlingCallback, bc.errorHandlingCallback,
bc.errorFuncName, bc.errorFuncName,
Call(LiteralModel("%last_error%") :: LiteralModel(i.toString) :: Nil, None), Call(
LiteralModel("%last_error%", LiteralType.string) :: LiteralModel(
i.toString,
LiteralType.number
) :: Nil,
None
),
Option(on) Option(on)
) )
) )

View File

@ -12,7 +12,7 @@ class TransformSpec extends AnyFlatSpec with Matchers {
"transform.forClient" should "work well with function 1 (no calls before on)" in { "transform.forClient" should "work well with function 1 (no calls before on)" in {
val ret = LiteralModel("\"return this\"") val ret = LiteralModel.quote("return this")
val func: FuncCallable = val func: FuncCallable =
FuncCallable( FuncCallable(
@ -64,7 +64,7 @@ class TransformSpec extends AnyFlatSpec with Matchers {
"transform.forClient" should "work well with function 2 (with a call before on)" in { "transform.forClient" should "work well with function 2 (with a call before on)" in {
val ret = LiteralModel("\"return this\"") val ret = LiteralModel.quote("return this")
val func: FuncCallable = FuncCallable( val func: FuncCallable = FuncCallable(
"ret", "ret",
@ -112,7 +112,7 @@ class TransformSpec extends AnyFlatSpec with Matchers {
FuncOp( FuncOp(
Node( Node(
CallServiceTag( CallServiceTag(
LiteralModel("\"srv1\""), LiteralModel.quote("srv1"),
"foo", "foo",
Call(Nil, Some(Call.Export("v", ScalarType.string))), Call(Nil, Some(Call.Export("v", ScalarType.string))),
None None
@ -146,7 +146,7 @@ class TransformSpec extends AnyFlatSpec with Matchers {
dataCall(bc, "-relay-", initPeer), dataCall(bc, "-relay-", initPeer),
Node( Node(
CallServiceTag( CallServiceTag(
LiteralModel("\"srv1\""), LiteralModel.quote("srv1"),
"foo", "foo",
Call(Nil, Some(Call.Export("v", ScalarType.string))), Call(Nil, Some(Call.Export("v", ScalarType.string))),
Some(initPeer) Some(initPeer)

View File

@ -4,7 +4,7 @@ import aqua.parser.lexer.Token._
import aqua.parser.lift.LiftParser import aqua.parser.lift.LiftParser
import aqua.parser.lift.LiftParser._ import aqua.parser.lift.LiftParser._
import cats.data.NonEmptyList import cats.data.NonEmptyList
import cats.parse.{Parser => P} import cats.parse.{Numbers, Parser => P, Parser0 => P0}
import cats.syntax.comonad._ import cats.syntax.comonad._
import cats.syntax.functor._ import cats.syntax.functor._
import cats.{Comonad, Functor} import cats.{Comonad, Functor}
@ -17,6 +17,12 @@ case class IntoField[F[_]: Comonad](name: F[String]) extends LambdaOp[F] {
def value: String = name.extract def value: String = name.extract
} }
case class IntoIndex[F[_]: Comonad](idx: F[Int]) extends LambdaOp[F] {
override def as[T](v: T): F[T] = idx.as(v)
def value: Int = idx.extract
}
case class IntoArray[F[_]: Functor](override val unit: F[Unit]) extends LambdaOp[F] { case class IntoArray[F[_]: Functor](override val unit: F[Unit]) extends LambdaOp[F] {
override def as[T](v: T): F[T] = unit.as(v) override def as[T](v: T): F[T] = unit.as(v)
} }
@ -25,10 +31,16 @@ object LambdaOp {
private def parseField[F[_]: LiftParser: Comonad]: P[LambdaOp[F]] = private def parseField[F[_]: LiftParser: Comonad]: P[LambdaOp[F]] =
(`.` *> `name`).lift.map(IntoField(_)) (`.` *> `name`).lift.map(IntoField(_))
private def parseArr[F[_]: LiftParser: Comonad]: P[LambdaOp[F]] = `*`.lift.map(IntoArray(_)) private def parseArr[F[_]: LiftParser: Comonad]: P[LambdaOp[F]] = `*`.lift.map(IntoArray(_))
private val intP0: P0[Int] = Numbers.nonNegativeIntString.map(_.toInt).?.map(_.getOrElse(0))
private def parseIdx[F[_]: LiftParser: Comonad]: P[LambdaOp[F]] =
((`!`: P[Unit]) *> intP0).lift.map(IntoIndex(_))
private def parseOp[F[_]: LiftParser: Comonad]: P[LambdaOp[F]] = private def parseOp[F[_]: LiftParser: Comonad]: P[LambdaOp[F]] =
P.oneOf(parseField.backtrack :: parseArr :: Nil) P.oneOf(parseField.backtrack :: parseArr :: parseIdx :: Nil)
def ops[F[_]: LiftParser: Comonad]: P[NonEmptyList[LambdaOp[F]]] = def ops[F[_]: LiftParser: Comonad]: P[NonEmptyList[LambdaOp[F]]] =
parseOp.rep parseOp.rep

View File

@ -58,6 +58,7 @@ object Token {
val `.` : P[Unit] = P.char('.') val `.` : P[Unit] = P.char('.')
val `"` : P[Unit] = P.char('"') val `"` : P[Unit] = P.char('"')
val `*` : P[Unit] = P.char('*') val `*` : P[Unit] = P.char('*')
val `!` : P[Unit] = P.char('!')
val `[]` : P[Unit] = P.string("[]") val `[]` : P[Unit] = P.string("[]")
val `(` : P[Unit] = P.char('(').surroundedBy(` `.?) val `(` : P[Unit] = P.char('(').surroundedBy(` `.?)
val `)` : P[Unit] = P.char(')').surroundedBy(` `.?) val `)` : P[Unit] = P.char(')').surroundedBy(` `.?)

View File

@ -10,6 +10,7 @@ import aqua.semantics.rules.abilities.AbilitiesAlgebra
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.{ArrowType, ScalarType, StreamType, Type} import aqua.types.{ArrowType, ScalarType, StreamType, Type}
import cats.Traverse
import cats.free.Free import cats.free.Free
import cats.syntax.apply._ import cats.syntax.apply._
import cats.syntax.flatMap._ import cats.syntax.flatMap._
@ -41,13 +42,7 @@ class CallArrowSem[F[_]](val expr: CallArrowExpr[F]) extends AnyVal {
} }
) )
) >>= { (v: Option[Type]) => ) >>= { (v: Option[Type]) =>
args Traverse[List].traverse(args)(V.valueToModel).map(_.flatten).map(_ -> v)
.foldLeft(Free.pure[Alg, List[ValueModel]](Nil)) { case (acc, v) =>
(acc, V.resolveType(v)).mapN((a, b) =>
a ++ b.map(bt => ValuesAlgebra.valueToModel(v, bt))
)
}
.map(_ -> v)
} }
private def toModel[Alg[_]](implicit private def toModel[Alg[_]](implicit
@ -63,18 +58,21 @@ class CallArrowSem[F[_]](val expr: CallArrowExpr[F]) extends AnyVal {
Option(at -> sid) // Here we assume that Ability is a Service that must be resolved Option(at -> sid) // Here we assume that Ability is a Service that must be resolved
case _ => None case _ => None
}.flatMap(_.fold(Free.pure[Alg, Option[FuncOp]](None)) { case (arrowType, serviceId) => }.flatMap(_.fold(Free.pure[Alg, Option[FuncOp]](None)) { case (arrowType, serviceId) =>
checkArgsRes(arrowType).map { case (argsResolved, t) => (checkArgsRes(arrowType), V.valueToModel(serviceId)).mapN {
FuncOp.leaf( case ((argsResolved, t), Some(serviceIdM)) =>
CallServiceTag( Option(
// TODO service id type should not be hardcoded FuncOp.leaf(
serviceId = ValuesAlgebra.valueToModel(serviceId, ScalarType.string), CallServiceTag(
funcName = funcName.value, serviceId = serviceIdM,
Call(argsResolved, (variable.map(_.value), t).mapN(Call.Export)) funcName = funcName.value,
Call(argsResolved, (variable.map(_.value), t).mapN(Call.Export))
)
)
) )
)
case _ => None
} }
.map(Option(_))
}) })
case None => case None =>
N.readArrow(funcName) N.readArrow(funcName)

View File

@ -18,9 +18,9 @@ class ConstantSem[F[_]](val expr: ConstantExpr[F]) extends AnyVal {
): Prog[Alg, Model] = { ): Prog[Alg, Model] = {
for { for {
defined <- N.constantDefined(expr.name) defined <- N.constantDefined(expr.name)
t <- V.resolveType(expr.value) v <- V.valueToModel(expr.value)
model <- (defined, t, expr.skipIfAlreadyDefined) match { model <- (defined, v.map(v => v -> v.lastType), expr.skipIfAlreadyDefined) match {
case (Some(definedType), Some(actualType), true) => case (Some(definedType), Some((vm, actualType)), true) =>
T.ensureTypeMatches(expr.value, definedType, actualType).map { T.ensureTypeMatches(expr.value, definedType, actualType).map {
case true => case true =>
Model.empty(s"Constant with name ${expr.name} was already defined, skipping") Model.empty(s"Constant with name ${expr.name} was already defined, skipping")
@ -32,9 +32,9 @@ class ConstantSem[F[_]](val expr: ConstantExpr[F]) extends AnyVal {
case (_, None, _) => case (_, None, _) =>
Free.pure[Alg, Model](Model.error(s"There is no such variable ${expr.value}")) Free.pure[Alg, Model](Model.error(s"There is no such variable ${expr.value}"))
case (_, Some(t), _) => case (_, Some(t), _) =>
N.defineConstant(expr.name, t) as (ConstantModel( N.defineConstant(expr.name, t._2) as (ConstantModel(
expr.name.value, expr.name.value,
ValuesAlgebra.valueToModel(expr.value, t) t._1
): Model) ): Model)
} }
} yield model } yield model

View File

@ -1,6 +1,6 @@
package aqua.semantics.expr package aqua.semantics.expr
import aqua.model.Model import aqua.model.{Model, ValueModel}
import aqua.model.func.body._ import aqua.model.func.body._
import aqua.parser.expr.ForExpr import aqua.parser.expr.ForExpr
import aqua.semantics.Prog import aqua.semantics.Prog
@ -21,20 +21,24 @@ class ForSem[F[_]](val expr: ForExpr[F]) extends AnyVal {
T: TypesAlgebra[F, Alg] T: TypesAlgebra[F, Alg]
): Prog[Alg, Model] = ): Prog[Alg, Model] =
Prog.around( Prog.around(
N.beginScope(expr.item) >> V.resolveType(expr.iterable).flatMap[Option[Type]] { N.beginScope(expr.item) >> V.valueToModel(expr.iterable).flatMap[Option[ValueModel]] {
case Some(at @ ArrayType(t)) => case Some(vm) =>
N.define(expr.item, t).as(Option(at)) vm.lastType match {
case Some(st @ StreamType(t)) => case at @ ArrayType(t) =>
N.define(expr.item, t).as(Option(st)) N.define(expr.item, t).as(Option(vm))
case Some(dt: Type) => case st @ StreamType(t) =>
T.ensureTypeMatches(expr.iterable, ArrayType(dt), dt).as(Option.empty[Type]) N.define(expr.item, t).as(Option(vm))
case _ => Free.pure[Alg, Option[Type]](None) case dt =>
T.ensureTypeMatches(expr.iterable, ArrayType(dt), dt).as(Option.empty[ValueModel])
}
case _ => Free.pure[Alg, Option[ValueModel]](None)
}, },
(stOpt: Option[Type], ops: Model) => (stOpt: Option[ValueModel], ops: Model) =>
N.endScope() as ((stOpt, ops) match { N.endScope() as ((stOpt, ops) match {
case (Some(t), op: FuncOp) => case (Some(vm), op: FuncOp) =>
FuncOp.wrap( FuncOp.wrap(
ForTag(expr.item.value, ValuesAlgebra.valueToModel(expr.iterable, t)), ForTag(expr.item.value, vm),
FuncOp.node( FuncOp.node(
expr.mode.map(_._2).fold[OpTag](SeqTag) { expr.mode.map(_._2).fold[OpTag](SeqTag) {
case ForExpr.ParMode => ParTag case ForExpr.ParMode => ParTag

View File

@ -1,8 +1,8 @@
package aqua.semantics.expr package aqua.semantics.expr
import aqua.model.func.body.FuncOp import aqua.model.func.body.FuncOp
import aqua.model.Model import aqua.model.{Model, ValueModel}
import aqua.model.func.{ArgDef, ArgsDef, Call, FuncModel} import aqua.model.func.{ArgDef, ArgsDef, FuncModel}
import aqua.parser.expr.FuncExpr import aqua.parser.expr.FuncExpr
import aqua.parser.lexer.Arg import aqua.parser.lexer.Arg
import aqua.semantics.Prog import aqua.semantics.Prog
@ -16,7 +16,6 @@ import cats.data.Chain
import cats.free.Free import cats.free.Free
import cats.syntax.flatMap._ import cats.syntax.flatMap._
import cats.syntax.functor._ import cats.syntax.functor._
import cats.syntax.apply._
class FuncSem[F[_]](val expr: FuncExpr[F]) extends AnyVal { class FuncSem[F[_]](val expr: FuncExpr[F]) extends AnyVal {
import expr._ import expr._
@ -62,15 +61,15 @@ class FuncSem[F[_]](val expr: FuncExpr[F]) extends AnyVal {
// Check return value type // Check return value type
((funcArrow.res, retValue) match { ((funcArrow.res, retValue) match {
case (Some(t), Some(v)) => case (Some(t), Some(v)) =>
V.resolveType(v).flatTap { V.valueToModel(v).flatTap {
case Some(vt) => T.ensureTypeMatches(v, t, vt).void case Some(vt) => T.ensureTypeMatches(v, t, vt.lastType).void
case None => Free.pure[Alg, Unit](()) case None => Free.pure[Alg, Unit](())
} }
case _ => case _ =>
Free.pure[Alg, Option[Type]](None) Free.pure[Alg, Option[ValueModel]](None)
// Erase arguments and internal variables // Erase arguments and internal variables
}).flatMap(retType => }).flatMap(retModel =>
A.endScope() >> N.endScope() >> (bodyGen match { A.endScope() >> N.endScope() >> (bodyGen match {
case bg: FuncOp if ret.isDefined == retValue.isDefined => case bg: FuncOp if ret.isDefined == retValue.isDefined =>
val argNames = args.map(_.name.value) val argNames = args.map(_.name.value)
@ -85,9 +84,7 @@ class FuncSem[F[_]](val expr: FuncExpr[F]) extends AnyVal {
case (n, at: ArrowType) => ArgDef.Arrow(n, at) case (n, at: ArrowType) => ArgDef.Arrow(n, at)
} }
), ),
ret = (retValue, retType, funcArrow.res).mapN { case (retV, retT, resT) => ret = retModel zip funcArrow.res,
ValuesAlgebra.valueToModel(retV, retT) -> resT
},
body = bg body = bg
) )

View File

@ -1,6 +1,6 @@
package aqua.semantics.expr package aqua.semantics.expr
import aqua.model.Model import aqua.model.{Model, ValueModel}
import aqua.model.func.body.{FuncOp, MatchMismatchTag, XorTag} import aqua.model.func.body.{FuncOp, MatchMismatchTag, XorTag}
import aqua.parser.expr.IfExpr import aqua.parser.expr.IfExpr
import aqua.semantics.rules.ValuesAlgebra import aqua.semantics.rules.ValuesAlgebra
@ -17,19 +17,19 @@ class IfSem[F[_]](val expr: IfExpr[F]) extends AnyVal {
T: TypesAlgebra[F, Alg] T: TypesAlgebra[F, Alg]
): Prog[Alg, Model] = ): Prog[Alg, Model] =
Prog.around( Prog.around(
V.resolveType(expr.left).flatMap { V.valueToModel(expr.left).flatMap {
case Some(lt) => case Some(lt) =>
V.resolveType(expr.right).flatMap { V.valueToModel(expr.right).flatMap {
case Some(rt) => case Some(rt) =>
T.ensureTypeMatches(expr.right, lt, rt) T.ensureTypeMatches(expr.right, lt.lastType, rt.lastType)
.map(m => Some(lt -> rt).filter(_ => m)) .map(m => Some(lt -> rt).filter(_ => m))
case None => case None =>
Free.pure[Alg, Option[(Type, Type)]](None) Free.pure[Alg, Option[(ValueModel, ValueModel)]](None)
} }
case None => case None =>
V.resolveType(expr.right).as[Option[(Type, Type)]](None) V.resolveType(expr.right).as[Option[(ValueModel, ValueModel)]](None)
}, },
(r: Option[(Type, Type)], ops: Model) => (r: Option[(ValueModel, ValueModel)], ops: Model) =>
r.fold(Free.pure[Alg, Model](Model.error("If expression errored in matching types"))) { r.fold(Free.pure[Alg, Model](Model.error("If expression errored in matching types"))) {
case (lt, rt) => case (lt, rt) =>
ops match { ops match {
@ -39,8 +39,8 @@ class IfSem[F[_]](val expr: IfExpr[F]) extends AnyVal {
XorTag.LeftBiased, XorTag.LeftBiased,
FuncOp.wrap( FuncOp.wrap(
MatchMismatchTag( MatchMismatchTag(
ValuesAlgebra.valueToModel(expr.left, lt), lt,
ValuesAlgebra.valueToModel(expr.right, rt), rt,
expr.eqOp.value expr.eqOp.value
), ),
op op

View File

@ -6,10 +6,11 @@ import aqua.parser.expr.OnExpr
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
import aqua.types.ScalarType import cats.Traverse
import cats.data.Chain import cats.data.Chain
import cats.free.Free
import cats.syntax.apply._
import cats.syntax.flatMap._ import cats.syntax.flatMap._
import cats.syntax.functor._
class OnSem[F[_]](val expr: OnExpr[F]) extends AnyVal { class OnSem[F[_]](val expr: OnExpr[F]) extends AnyVal {
@ -25,16 +26,25 @@ class OnSem[F[_]](val expr: OnExpr[F]) extends AnyVal {
} }
>> A.beginScope(expr.peerId), >> A.beginScope(expr.peerId),
(_: Unit, ops: Model) => (_: Unit, ops: Model) =>
A.endScope() as (ops match { A.endScope() >> (ops match {
case op: FuncOp => case op: FuncOp =>
FuncOp.wrap( (
OnTag( V.valueToModel(expr.peerId),
ValuesAlgebra.valueToModel(expr.peerId, ScalarType.string), Traverse[List].traverse(expr.via)(V.valueToModel).map(_.flatten)
Chain.fromSeq(expr.via).map(ValuesAlgebra.valueToModel(_, ScalarType.string)) ).mapN {
), case (Some(om), via) =>
op FuncOp.wrap(
) OnTag(
case m => Model.error("On body is not an op, it's " + m) om,
Chain.fromSeq(via)
),
op
)
case _ =>
Model.error("OnSem: Impossible error")
}
case m => Free.pure[Alg, Model](Model.error("On body is not an op, it's " + m))
}) })
) )
} }

View File

@ -4,7 +4,7 @@ import aqua.model._
import aqua.parser.lexer._ import aqua.parser.lexer._
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.{ArrowType, LiteralType, StreamType, Type} import aqua.types.{ArrowType, LiteralType, Type}
import cats.data.Chain import cats.data.Chain
import cats.free.Free import cats.free.Free
import cats.syntax.apply._ import cats.syntax.apply._
@ -29,13 +29,15 @@ class ValuesAlgebra[F[_], Alg[_]](implicit N: NamesAlgebra[F, Alg], T: TypesAlge
} }
def resolveType(v: Value[F]): Free[Alg, Option[Type]] = def resolveType(v: Value[F]): Free[Alg, Option[Type]] =
valueToModel(v).map(_.map(_.lastType))
def valueToModel(v: Value[F]): Free[Alg, Option[ValueModel]] =
v match { v match {
case l: Literal[F] => case l: Literal[F] => Free.pure(Some(LiteralModel(l.value, l.ts)))
Free pure [Alg, Option[Type]] Some(l.ts) case VarLambda(name, ops) =>
case VarLambda(n, lambda) => N.read(name).flatMap {
N.read(n).flatMap {
case Some(t) => case Some(t) =>
T.resolveLambda(t, lambda) T.resolveLambda(t, ops).map(Chain.fromSeq).map(VarModel(name.value, t, _)).map(Some(_))
case None => case None =>
Free.pure(None) Free.pure(None)
} }
@ -46,14 +48,9 @@ class ValuesAlgebra[F[_], Alg[_]](implicit N: NamesAlgebra[F, Alg], T: TypesAlge
case false => Free.pure[Alg, Boolean](false) case false => Free.pure[Alg, Boolean](false)
case true => case true =>
args args
.map[Free[Alg, Option[(Token[F], Type)]]] { .map[Free[Alg, Option[(Token[F], Type)]]](tkn =>
case l: Literal[F] => Free.pure(Some(l -> l.ts)) resolveType(tkn).map(_.map(t => tkn -> t))
case VarLambda(n, ops) => )
N.read(n).flatMap {
case Some(t) => T.resolveLambda(t, ops).map(_.map(ops.lastOption.getOrElse(n) -> _))
case None => Free.pure(None)
}
}
.zip(arr.args) .zip(arr.args)
.foldLeft( .foldLeft(
Free.pure[Alg, Boolean](true) Free.pure[Alg, Boolean](true)
@ -75,19 +72,6 @@ class ValuesAlgebra[F[_], Alg[_]](implicit N: NamesAlgebra[F, Alg], T: TypesAlge
object ValuesAlgebra { object ValuesAlgebra {
private def opsToModel[F[_]](ops: List[LambdaOp[F]]): Chain[LambdaModel] =
ops match {
case Nil => Chain.empty
case (_: IntoArray[F]) :: tail => opsToModel(tail).prepend(IntoArrayModel)
case (f: IntoField[F]) :: tail => opsToModel(tail).prepend(IntoFieldModel(f.value))
}
def valueToModel[F[_]](v: Value[F], t: Type): ValueModel =
v match {
case l: Literal[F] => LiteralModel(l.value)
case VarLambda(name, ops) => VarModel(name.value, t, opsToModel(ops))
}
implicit def deriveValuesAlgebra[F[_], Alg[_]](implicit implicit def deriveValuesAlgebra[F[_], Alg[_]](implicit
N: NamesAlgebra[F, Alg], N: NamesAlgebra[F, Alg],
T: TypesAlgebra[F, Alg] T: TypesAlgebra[F, Alg]

View File

@ -1,5 +1,6 @@
package aqua.semantics.rules.types package aqua.semantics.rules.types
import aqua.model.LambdaModel
import aqua.parser.lexer.{ArrowTypeToken, CustomTypeToken, LambdaOp, Name, Token, TypeToken} import aqua.parser.lexer.{ArrowTypeToken, CustomTypeToken, LambdaOp, Name, Token, TypeToken}
import aqua.types.{ArrowType, Type} import aqua.types.{ArrowType, Type}
import cats.data.NonEmptyMap import cats.data.NonEmptyMap
@ -16,7 +17,8 @@ case class DefineDataType[F[_]](name: CustomTypeToken[F], fields: NonEmptyMap[St
extends TypeOp[F, Boolean] extends TypeOp[F, Boolean]
case class DefineAlias[F[_]](name: CustomTypeToken[F], target: Type) extends TypeOp[F, Boolean] case class DefineAlias[F[_]](name: CustomTypeToken[F], target: Type) extends TypeOp[F, Boolean]
case class ResolveLambda[F[_]](root: Type, ops: List[LambdaOp[F]]) extends TypeOp[F, Option[Type]] case class ResolveLambda[F[_]](root: Type, ops: List[LambdaOp[F]])
extends TypeOp[F, List[LambdaModel]]
case class EnsureTypeMatches[F[_]](token: Token[F], expected: Type, given: Type) case class EnsureTypeMatches[F[_]](token: Token[F], expected: Type, given: Type)
extends TypeOp[F, Boolean] extends TypeOp[F, Boolean]

View File

@ -1,5 +1,6 @@
package aqua.semantics.rules.types package aqua.semantics.rules.types
import aqua.model.LambdaModel
import aqua.parser.lexer.{ArrowTypeToken, CustomTypeToken, LambdaOp, Name, Token, TypeToken} import aqua.parser.lexer.{ArrowTypeToken, CustomTypeToken, LambdaOp, Name, Token, TypeToken}
import aqua.types.{ArrowType, Type} import aqua.types.{ArrowType, Type}
import cats.InjectK import cats.InjectK
@ -29,7 +30,7 @@ class TypesAlgebra[F[_], Alg[_]](implicit T: InjectK[TypeOp[F, *], Alg]) {
def defineAlias(name: CustomTypeToken[F], target: Type): Free[Alg, Boolean] = def defineAlias(name: CustomTypeToken[F], target: Type): Free[Alg, Boolean] =
Free.liftInject[Alg](DefineAlias(name, target)) Free.liftInject[Alg](DefineAlias(name, target))
def resolveLambda(root: Type, ops: List[LambdaOp[F]]): Free[Alg, Option[Type]] = def resolveLambda(root: Type, ops: List[LambdaOp[F]]): Free[Alg, List[LambdaModel]] =
Free.liftInject[Alg](ResolveLambda(root, ops)) Free.liftInject[Alg](ResolveLambda(root, ops))
def ensureTypeMatches(token: Token[F], expected: Type, given: Type): Free[Alg, Boolean] = def ensureTypeMatches(token: Token[F], expected: Type, given: Type): Free[Alg, Boolean] =

View File

@ -94,8 +94,8 @@ class TypesInterpreter[F[_], X](implicit lens: Lens[X, TypesState[F]], error: Re
case rl: ResolveLambda[F] => case rl: ResolveLambda[F] =>
getState.map(_.resolveOps(rl.root, rl.ops)).flatMap { getState.map(_.resolveOps(rl.root, rl.ops)).flatMap {
case Left((tkn, hint)) => report(tkn, hint).as(None) case Left((tkn, hint)) => report(tkn, hint).as(Nil)
case Right(t) => State.pure(Some(t)) case Right(ts) => State.pure(ts)
} }
case etm: EnsureTypeMatches[F] => case etm: EnsureTypeMatches[F] =>

View File

@ -1,5 +1,6 @@
package aqua.semantics.rules.types package aqua.semantics.rules.types
import aqua.model.{IntoArrayModel, IntoFieldModel, IntoIndexModel, LambdaModel}
import aqua.parser.lexer.{ import aqua.parser.lexer.{
ArrayTypeToken, ArrayTypeToken,
ArrowTypeToken, ArrowTypeToken,
@ -7,6 +8,7 @@ import aqua.parser.lexer.{
CustomTypeToken, CustomTypeToken,
IntoArray, IntoArray,
IntoField, IntoField,
IntoIndex,
LambdaOp, LambdaOp,
Name, Name,
StreamTypeToken, StreamTypeToken,
@ -69,22 +71,36 @@ case class TypesState[F[_]](
Invalid(NonEmptyChain.one(ad.resType.getOrElse(ad) -> "Cannot resolve the result type")) Invalid(NonEmptyChain.one(ad.resType.getOrElse(ad) -> "Cannot resolve the result type"))
} }
def resolveOps(rootT: Type, ops: List[LambdaOp[F]]): Either[(Token[F], String), Type] = def resolveOps(
ops.headOption.fold[Either[(Token[F], String), Type]](Right(rootT)) { rootT: Type,
case i @ IntoArray(f) => ops: List[LambdaOp[F]]
): Either[(Token[F], String), List[LambdaModel]] =
ops match {
case Nil => Right(Nil)
case (i @ IntoArray(_)) :: tail =>
rootT match { rootT match {
case ArrayType(intern) => resolveOps(intern, ops.tail).map[Type](ArrayType) case ArrayType(intern) =>
case _ => Left(i -> s"Expected $rootT to be an array") resolveOps(intern, tail).map(IntoArrayModel(ArrayType(intern)) :: _)
case StreamType(intern) =>
resolveOps(intern, tail).map(IntoArrayModel(ArrayType(intern)) :: _)
case _ => Left(i -> s"Expected $rootT to be an array or a stream")
} }
case i @ IntoField(name) => case (i @ IntoField(_)) :: tail =>
rootT match { rootT match {
case pt @ ProductType(_, fields) => case pt @ ProductType(_, fields) =>
fields(i.value) fields(i.value)
.toRight(i -> s"Field `${i.value}` not found in type `${pt.name}``") .toRight(i -> s"Field `${i.value}` not found in type `${pt.name}``")
.flatMap(resolveOps(_, ops.tail)) .flatMap(t => resolveOps(t, tail).map(IntoFieldModel(i.value, t) :: _))
case _ => Left(i -> s"Expected product to resolve a field, got $rootT") case _ => Left(i -> s"Expected product to resolve a field, got $rootT")
} }
case (i @ IntoIndex(_)) :: tail =>
rootT match {
case ArrayType(intern) =>
resolveOps(intern, tail).map(IntoIndexModel(i.value, intern) :: _)
case StreamType(intern) =>
resolveOps(intern, tail).map(IntoIndexModel(i.value, intern) :: _)
case _ => Left(i -> s"Expected $rootT to be an array")
}
} }
} }