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
run: |
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
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

View File

@ -13,15 +13,19 @@ service Test("test"):
getUserList: -> []User
doSomething: -> bool
func betterMessage(relay: string):
func betterMessage(relay: string, arr: []string) -> string:
on relay:
Peer.is_connected("something")
par isOnline <- Peer.is_connected(relay)
par on "quray":
Peer.is_connected("qurara")
stream: *string
if isOnline:
try:
Test.doSomething()
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 {
case Nil => ""
case IntoArrayModel :: tail =>
case IntoArrayModel(_) :: tail =>
s"[@${lambdaToString(tail)}]"
case IntoFieldModel(field) :: tail =>
case IntoFieldModel(field, _) :: tail =>
s".$field${lambdaToString(tail)}"
case IntoIndexModel(idx, _) :: tail =>
s".[$idx]${lambdaToString(tail)}"
}
def valueToData(vm: ValueModel): DataView = vm match {
case LiteralModel(value) => DataView.StringScalar(value)
case LiteralModel(value, _) => DataView.StringScalar(value)
case VarModel(name, t, lambda) =>
val n = t match {
case _: StreamType => "$" + name

View File

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

View File

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

View File

@ -8,7 +8,7 @@ import cats.free.Cofree
object FuncOps {
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 =
FuncOp.leaf(

View File

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

View File

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

View File

@ -3,7 +3,7 @@ package aqua.model
import aqua.model.func.Call
import aqua.model.func.body._
import aqua.model.transform.BodyConfig
import aqua.types.ScalarType
import aqua.types.{LiteralType, ScalarType}
import cats.Eval
import cats.data.Chain
import cats.free.Cofree
@ -84,24 +84,30 @@ object Node {
implicit def nodeToCof(tree: Node): Cof =
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 initPeer = LiteralModel.initPeerId
val emptyCall = Call(Nil, None)
val otherPeer = LiteralModel("other-peer")
val otherRelay = LiteralModel("other-relay")
val otherPeer2 = LiteralModel("other-peer-2")
val otherRelay2 = LiteralModel("other-relay-2")
val otherPeer = LiteralModel("other-peer", ScalarType.string)
val otherRelay = LiteralModel("other-relay", ScalarType.string)
val otherPeer2 = LiteralModel("other-peer-2", ScalarType.string)
val otherRelay2 = LiteralModel("other-relay-2", ScalarType.string)
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(
CallServiceTag(
bc.errorHandlingCallback,
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)
)
)

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 {
val ret = LiteralModel("\"return this\"")
val ret = LiteralModel.quote("return this")
val func: 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 {
val ret = LiteralModel("\"return this\"")
val ret = LiteralModel.quote("return this")
val func: FuncCallable = FuncCallable(
"ret",
@ -112,7 +112,7 @@ class TransformSpec extends AnyFlatSpec with Matchers {
FuncOp(
Node(
CallServiceTag(
LiteralModel("\"srv1\""),
LiteralModel.quote("srv1"),
"foo",
Call(Nil, Some(Call.Export("v", ScalarType.string))),
None
@ -146,7 +146,7 @@ class TransformSpec extends AnyFlatSpec with Matchers {
dataCall(bc, "-relay-", initPeer),
Node(
CallServiceTag(
LiteralModel("\"srv1\""),
LiteralModel.quote("srv1"),
"foo",
Call(Nil, Some(Call.Export("v", ScalarType.string))),
Some(initPeer)

View File

@ -4,7 +4,7 @@ import aqua.parser.lexer.Token._
import aqua.parser.lift.LiftParser
import aqua.parser.lift.LiftParser._
import cats.data.NonEmptyList
import cats.parse.{Parser => P}
import cats.parse.{Numbers, Parser => P, Parser0 => P0}
import cats.syntax.comonad._
import cats.syntax.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
}
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] {
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]] =
(`.` *> `name`).lift.map(IntoField(_))
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]] =
P.oneOf(parseField.backtrack :: parseArr :: Nil)
P.oneOf(parseField.backtrack :: parseArr :: parseIdx :: Nil)
def ops[F[_]: LiftParser: Comonad]: P[NonEmptyList[LambdaOp[F]]] =
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.string("[]")
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.types.TypesAlgebra
import aqua.types.{ArrowType, ScalarType, StreamType, Type}
import cats.Traverse
import cats.free.Free
import cats.syntax.apply._
import cats.syntax.flatMap._
@ -41,13 +42,7 @@ class CallArrowSem[F[_]](val expr: CallArrowExpr[F]) extends AnyVal {
}
)
) >>= { (v: Option[Type]) =>
args
.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)
Traverse[List].traverse(args)(V.valueToModel).map(_.flatten).map(_ -> v)
}
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
case _ => None
}.flatMap(_.fold(Free.pure[Alg, Option[FuncOp]](None)) { case (arrowType, serviceId) =>
checkArgsRes(arrowType).map { case (argsResolved, t) =>
(checkArgsRes(arrowType), V.valueToModel(serviceId)).mapN {
case ((argsResolved, t), Some(serviceIdM)) =>
Option(
FuncOp.leaf(
CallServiceTag(
// TODO service id type should not be hardcoded
serviceId = ValuesAlgebra.valueToModel(serviceId, ScalarType.string),
serviceId = serviceIdM,
funcName = funcName.value,
Call(argsResolved, (variable.map(_.value), t).mapN(Call.Export))
)
)
)
case _ => None
}
.map(Option(_))
})
case None =>
N.readArrow(funcName)

View File

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

View File

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

View File

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

View File

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

View File

@ -6,10 +6,11 @@ import aqua.parser.expr.OnExpr
import aqua.semantics.Prog
import aqua.semantics.rules.ValuesAlgebra
import aqua.semantics.rules.abilities.AbilitiesAlgebra
import aqua.types.ScalarType
import cats.Traverse
import cats.data.Chain
import cats.free.Free
import cats.syntax.apply._
import cats.syntax.flatMap._
import cats.syntax.functor._
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),
(_: Unit, ops: Model) =>
A.endScope() as (ops match {
A.endScope() >> (ops match {
case op: FuncOp =>
(
V.valueToModel(expr.peerId),
Traverse[List].traverse(expr.via)(V.valueToModel).map(_.flatten)
).mapN {
case (Some(om), via) =>
FuncOp.wrap(
OnTag(
ValuesAlgebra.valueToModel(expr.peerId, ScalarType.string),
Chain.fromSeq(expr.via).map(ValuesAlgebra.valueToModel(_, ScalarType.string))
om,
Chain.fromSeq(via)
),
op
)
case m => Model.error("On body is not an op, it's " + m)
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.semantics.rules.names.NamesAlgebra
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.free.Free
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]] =
valueToModel(v).map(_.map(_.lastType))
def valueToModel(v: Value[F]): Free[Alg, Option[ValueModel]] =
v match {
case l: Literal[F] =>
Free pure [Alg, Option[Type]] Some(l.ts)
case VarLambda(n, lambda) =>
N.read(n).flatMap {
case l: Literal[F] => Free.pure(Some(LiteralModel(l.value, l.ts)))
case VarLambda(name, ops) =>
N.read(name).flatMap {
case Some(t) =>
T.resolveLambda(t, lambda)
T.resolveLambda(t, ops).map(Chain.fromSeq).map(VarModel(name.value, t, _)).map(Some(_))
case 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 true =>
args
.map[Free[Alg, Option[(Token[F], Type)]]] {
case l: Literal[F] => Free.pure(Some(l -> l.ts))
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)
}
}
.map[Free[Alg, Option[(Token[F], Type)]]](tkn =>
resolveType(tkn).map(_.map(t => tkn -> t))
)
.zip(arr.args)
.foldLeft(
Free.pure[Alg, Boolean](true)
@ -75,19 +72,6 @@ class ValuesAlgebra[F[_], Alg[_]](implicit N: NamesAlgebra[F, Alg], T: TypesAlge
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
N: NamesAlgebra[F, Alg],
T: TypesAlgebra[F, Alg]

View File

@ -1,5 +1,6 @@
package aqua.semantics.rules.types
import aqua.model.LambdaModel
import aqua.parser.lexer.{ArrowTypeToken, CustomTypeToken, LambdaOp, Name, Token, TypeToken}
import aqua.types.{ArrowType, Type}
import cats.data.NonEmptyMap
@ -16,7 +17,8 @@ case class DefineDataType[F[_]](name: CustomTypeToken[F], fields: NonEmptyMap[St
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)
extends TypeOp[F, Boolean]

View File

@ -1,5 +1,6 @@
package aqua.semantics.rules.types
import aqua.model.LambdaModel
import aqua.parser.lexer.{ArrowTypeToken, CustomTypeToken, LambdaOp, Name, Token, TypeToken}
import aqua.types.{ArrowType, Type}
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] =
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))
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] =>
getState.map(_.resolveOps(rl.root, rl.ops)).flatMap {
case Left((tkn, hint)) => report(tkn, hint).as(None)
case Right(t) => State.pure(Some(t))
case Left((tkn, hint)) => report(tkn, hint).as(Nil)
case Right(ts) => State.pure(ts)
}
case etm: EnsureTypeMatches[F] =>

View File

@ -1,5 +1,6 @@
package aqua.semantics.rules.types
import aqua.model.{IntoArrayModel, IntoFieldModel, IntoIndexModel, LambdaModel}
import aqua.parser.lexer.{
ArrayTypeToken,
ArrowTypeToken,
@ -7,6 +8,7 @@ import aqua.parser.lexer.{
CustomTypeToken,
IntoArray,
IntoField,
IntoIndex,
LambdaOp,
Name,
StreamTypeToken,
@ -69,22 +71,36 @@ case class TypesState[F[_]](
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] =
ops.headOption.fold[Either[(Token[F], String), Type]](Right(rootT)) {
case i @ IntoArray(f) =>
def resolveOps(
rootT: Type,
ops: List[LambdaOp[F]]
): Either[(Token[F], String), List[LambdaModel]] =
ops match {
case Nil => Right(Nil)
case (i @ IntoArray(_)) :: tail =>
rootT match {
case ArrayType(intern) => resolveOps(intern, ops.tail).map[Type](ArrayType)
case _ => Left(i -> s"Expected $rootT to be an array")
case ArrayType(intern) =>
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 {
case pt @ ProductType(_, fields) =>
fields(i.value)
.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 (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")
}
}
}