mirror of
https://github.com/fluencelabs/aqua.git
synced 2024-12-04 22:50:18 +00:00
Basic AbilitiesInterpreter implementation
This commit is contained in:
parent
ef3fbec100
commit
c403830d59
@ -61,7 +61,7 @@ object Compiler {
|
||||
)
|
||||
}
|
||||
|
||||
type Alg0[F[_], A] = EitherK[AbilityOp.Aux[F, *], NameOp.Aux[F, *], A]
|
||||
type Alg0[F[_], A] = EitherK[AbilityOp[F, *], NameOp[F, *], A]
|
||||
type Alg1[F[_], A] = EitherK[PeerIdOp[F, *], Alg0[F, *], A]
|
||||
type Alg[F[_], A] = EitherK[TypeOp[F, *], Alg1[F, *], A]
|
||||
|
||||
|
@ -1,6 +1,10 @@
|
||||
package aqua.ast
|
||||
|
||||
case class Gen(log: String) {}
|
||||
import cats.free.Free
|
||||
|
||||
case class Gen(log: String) {
|
||||
def lift[F[_]]: Free[F, Gen] = Free.pure(this)
|
||||
}
|
||||
|
||||
object Gen {
|
||||
def noop = new Gen("noop")
|
||||
|
7
src/main/scala/aqua/ast/algebra/ReportError.scala
Normal file
7
src/main/scala/aqua/ast/algebra/ReportError.scala
Normal file
@ -0,0 +1,7 @@
|
||||
package aqua.ast.algebra
|
||||
|
||||
import aqua.parser.lexer.Token
|
||||
|
||||
trait ReportError[F[_], X] {
|
||||
def apply(st: X, token: Token[F], hint: String): X
|
||||
}
|
@ -6,33 +6,33 @@ import cats.InjectK
|
||||
import cats.data.{NonEmptyList, NonEmptyMap}
|
||||
import cats.free.Free
|
||||
|
||||
class AbilitiesAlgebra[F[_], Alg[_]](implicit A: InjectK[AbilityOp.Aux[F, *], Alg]) {
|
||||
class AbilitiesAlgebra[F[_], Alg[_]](implicit A: InjectK[AbilityOp[F, *], Alg]) {
|
||||
|
||||
def defineArrow(arrow: Name[F], `type`: ArrowType): Free[Alg, Unit] =
|
||||
Free.liftInject[Alg](DefineArrow[F](arrow, `type`): AbilityOp.Aux[F, Unit])
|
||||
def defineArrow(arrow: Name[F], `type`: ArrowType): Free[Alg, Boolean] =
|
||||
Free.liftInject[Alg](DefineArrow[F](arrow, `type`))
|
||||
|
||||
def purgeArrows(): Free[Alg, NonEmptyList[(Name[F], ArrowType)]] =
|
||||
Free.liftInject[Alg](PurgeArrows[F](): AbilityOp.Aux[F, NonEmptyList[(Name[F], ArrowType)]])
|
||||
def purgeArrows(token: Token[F]): Free[Alg, Option[NonEmptyList[(Name[F], ArrowType)]]] =
|
||||
Free.liftInject[Alg](PurgeArrows[F](token))
|
||||
|
||||
def defineService(name: Ability[F], arrows: NonEmptyMap[String, ArrowType]): Free[Alg, Unit] =
|
||||
Free.liftInject[Alg](DefineService[F](name, arrows): AbilityOp.Aux[F, Unit])
|
||||
def defineService(name: Ability[F], arrows: NonEmptyMap[String, ArrowType]): Free[Alg, Boolean] =
|
||||
Free.liftInject[Alg](DefineService[F](name, arrows))
|
||||
|
||||
def getArrow(name: Ability[F], arrow: Name[F]): Free[Alg, ArrowType] =
|
||||
Free.liftInject[Alg](GetArrow[F](name, arrow): AbilityOp.Aux[F, ArrowType])
|
||||
def getArrow(name: Ability[F], arrow: Name[F]): Free[Alg, Option[ArrowType]] =
|
||||
Free.liftInject[Alg](GetArrow[F](name, arrow))
|
||||
|
||||
def setServiceId(name: Ability[F], id: Value[F]): Free[Alg, Unit] =
|
||||
Free.liftInject[Alg](SetServiceId[F](name, id): AbilityOp.Aux[F, Unit])
|
||||
def setServiceId(name: Ability[F], id: Value[F]): Free[Alg, Boolean] =
|
||||
Free.liftInject[Alg](SetServiceId[F](name, id))
|
||||
|
||||
def beginScope(token: Token[F]): Free[Alg, Unit] =
|
||||
Free.liftInject[Alg](BeginScope[F](token): AbilityOp.Aux[F, Unit])
|
||||
Free.liftInject[Alg](BeginScope[F](token))
|
||||
|
||||
def endScope(): Free[Alg, Unit] =
|
||||
Free.liftInject[Alg](EndScope[F](): AbilityOp.Aux[F, Unit])
|
||||
def endScope(): Free[Alg, Boolean] =
|
||||
Free.liftInject[Alg](EndScope[F]())
|
||||
|
||||
}
|
||||
|
||||
object AbilitiesAlgebra {
|
||||
|
||||
implicit def abilitiesAlgebra[F[_], Alg[_]](implicit A: InjectK[AbilityOp.Aux[F, *], Alg]): AbilitiesAlgebra[F, Alg] =
|
||||
implicit def abilitiesAlgebra[F[_], Alg[_]](implicit A: InjectK[AbilityOp[F, *], Alg]): AbilitiesAlgebra[F, Alg] =
|
||||
new AbilitiesAlgebra[F, Alg]()
|
||||
}
|
||||
|
@ -1,71 +1,115 @@
|
||||
package aqua.ast.algebra.abilities
|
||||
|
||||
import aqua.AquaError
|
||||
import aqua.ast.algebra.ReportError
|
||||
import aqua.ast.algebra.types.ArrowType
|
||||
import aqua.parser.lexer.{Name, Token}
|
||||
import cats.data.{EitherT, NonEmptyList, NonEmptyMap, State}
|
||||
import cats.~>
|
||||
import aqua.parser.lexer.{Name, Token, Value}
|
||||
import cats.data.{NonEmptyList, NonEmptyMap, State}
|
||||
import cats.{~>, Comonad}
|
||||
import shapeless.Lens
|
||||
import cats.syntax.functor._
|
||||
import cats.syntax.comonad._
|
||||
|
||||
class AbilitiesInterpreter[F[_], X](implicit lens: Lens[X, AbState[F]]) extends (AbilityOp.Aux[F, *] ~> State[X, *]) {
|
||||
class AbilitiesInterpreter[F[_]: Comonad, X](implicit lens: Lens[X, AbState[F]], error: ReportError[F, X])
|
||||
extends (AbilityOp[F, *] ~> State[X, *]) {
|
||||
|
||||
type S[A] = State[X, A]
|
||||
|
||||
private def getState: S[AbState[F]] = State.get.map(lens.get)
|
||||
private def setState(st: AbState[F]): S[Unit] = State.modify(s => lens.set(s)(st))
|
||||
|
||||
private def report(t: Token[F], hint: String): S[Unit] =
|
||||
State.modify(error(_, t, hint))
|
||||
|
||||
private def modify(f: AbState[F] => AbState[F]): S[Unit] =
|
||||
State.modify(s => lens.set(s)(f(lens.get(s))))
|
||||
|
||||
override def apply[A](fa: AbilityOp.Aux[F, A]): State[X, A] =
|
||||
override def apply[A](fa: AbilityOp[F, A]): State[X, A] =
|
||||
(fa match {
|
||||
case bs: BeginScope[F] =>
|
||||
modify(_.beginScope(bs.token))
|
||||
|
||||
case EndScope() =>
|
||||
modify(_.endScope)
|
||||
|
||||
case PurgeArrows() =>
|
||||
case pa: PurgeArrows[F] =>
|
||||
getState.map(_.purgeArrows).flatMap {
|
||||
case (Some(arrs), nextState) => setState(nextState).as(arrs)
|
||||
case _ => ??? //setError
|
||||
case Some((arrs, nextState)) => setState(nextState).as(Option[NonEmptyList[(Name[F], ArrowType)]](arrs))
|
||||
case _ =>
|
||||
report(pa.token, "Cannot purge arrows, no arrows provided")
|
||||
.as(Option.empty[NonEmptyList[(Name[F], ArrowType)]])
|
||||
}
|
||||
|
||||
case GetArrow(name, arrow) =>
|
||||
// Find the scope with ability
|
||||
// get ability arrows
|
||||
// find arrow by name
|
||||
// if no matching arrow, error
|
||||
???
|
||||
case SetServiceId(name, id) =>
|
||||
// in current scope, set service id by its name
|
||||
// check that it's registered, and that it is a service
|
||||
case DefineArrow(arrow, t) =>
|
||||
// in current scope, save arrow in the cache
|
||||
// if an arrow with this name already exists, raise
|
||||
???
|
||||
case ga: GetArrow[F] =>
|
||||
getState.map(_.services.get(ga.name.name.extract)).flatMap {
|
||||
case Some(arrows) =>
|
||||
arrows(ga.arrow.name.extract)
|
||||
.fold(
|
||||
report(
|
||||
ga.arrow,
|
||||
s"Service found, but arrow is undefined, available: ${arrows.value.keys.toNonEmptyList.toList.mkString(", ")}"
|
||||
).as(Option.empty[ArrowType])
|
||||
)(a => State.pure(Some(a)))
|
||||
case None =>
|
||||
report(ga.name, "Ability with this name is undefined").as(Option.empty[ArrowType])
|
||||
}
|
||||
|
||||
case DefineService(name, arrows) =>
|
||||
// in current scope, define a service (or do it globally?)
|
||||
// in case service name is already used for another ability, raise
|
||||
???
|
||||
case s: SetServiceId[F] =>
|
||||
getState.map(_.services.get(s.name.name.extract)).flatMap {
|
||||
case Some(_) =>
|
||||
getState.map(_.stack).flatMap {
|
||||
case h :: tail =>
|
||||
modify(_.copy(stack = h.copy(serviceIds = h.serviceIds.updated(s.name.name.extract, s.id)) :: tail))
|
||||
.as(true)
|
||||
|
||||
case _ =>
|
||||
report(s.name, "Trying to set service ID while out of the scope").as(false)
|
||||
}
|
||||
case None =>
|
||||
report(s.name, "Service with this name is not registered, can't set its ID").as(false)
|
||||
}
|
||||
|
||||
case da: DefineArrow[F] =>
|
||||
getState.map(_.stack).flatMap {
|
||||
case h :: tail =>
|
||||
h.arrows.get(da.arrow.name.extract) match {
|
||||
case Some(_) => report(da.arrow, "Arrow with this name was already defined above").as(false)
|
||||
case None =>
|
||||
modify(
|
||||
_.copy(stack =
|
||||
h.copy(arrows = h.arrows.updated(da.arrow.name.extract, da.arrow -> da.`type`)) :: tail
|
||||
)
|
||||
).as(true)
|
||||
}
|
||||
case _ =>
|
||||
report(da.arrow, "No abilities definition scope is found").as(false)
|
||||
}
|
||||
|
||||
case ds: DefineService[F] =>
|
||||
getState.map(_.services.get(ds.name.name.extract)).flatMap {
|
||||
case Some(_) => report(ds.name, "Service with this name was already defined").as(false)
|
||||
case None => modify(s => s.copy(services = s.services.updated(ds.name.name.extract, ds.arrows))).as(true)
|
||||
}
|
||||
|
||||
}).asInstanceOf[State[X, A]]
|
||||
}
|
||||
|
||||
case class AbState[F[_]](stack: List[AbScope[F]]) {
|
||||
case class AbState[F[_]](
|
||||
stack: List[AbScope[F]] = Nil,
|
||||
services: Map[String, NonEmptyMap[String, ArrowType]] = Map.empty
|
||||
) {
|
||||
def beginScope(token: Token[F]): AbState[F] = copy[F](AbScope[F](token) :: stack)
|
||||
def endScope: AbState[F] = copy[F](stack.tail)
|
||||
|
||||
def purgeArrows: (Option[NonEmptyList[(Name[F], ArrowType)]], AbState[F]) =
|
||||
def purgeArrows: Option[(NonEmptyList[(Name[F], ArrowType)], AbState[F])] =
|
||||
stack match {
|
||||
case sc :: tail =>
|
||||
NonEmptyList.fromList(sc.arrows.values.toList) -> copy[F](sc.copy(arrows = Map.empty) :: tail)
|
||||
case _ => None -> this
|
||||
NonEmptyList.fromList(sc.arrows.values.toList).map(_ -> copy[F](sc.copy(arrows = Map.empty) :: tail))
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
|
||||
case class AbScope[F[_]](
|
||||
token: Token[F],
|
||||
arrows: Map[String, (Name[F], ArrowType)] = Map.empty[String, (Name[F], ArrowType)]
|
||||
arrows: Map[String, (Name[F], ArrowType)] = Map.empty[String, (Name[F], ArrowType)],
|
||||
serviceIds: Map[String, Value[F]] = Map.empty[String, Value[F]]
|
||||
)
|
||||
|
@ -4,46 +4,18 @@ import aqua.ast.algebra.types.ArrowType
|
||||
import aqua.parser.lexer.{Ability, Name, Token, Value}
|
||||
import cats.data.{NonEmptyList, NonEmptyMap}
|
||||
|
||||
sealed trait AbilityOp[T] {
|
||||
type CF[_]
|
||||
}
|
||||
sealed trait AbilityOp[F[_], T]
|
||||
|
||||
object AbilityOp {
|
||||
case class DefineArrow[F[_]](arrow: Name[F], `type`: ArrowType) extends AbilityOp[F, Boolean]
|
||||
|
||||
type Aux[F[_], T] = AbilityOp[T] {
|
||||
type CF[A] = F[A]
|
||||
}
|
||||
case class PurgeArrows[F[_]](token: Token[F]) extends AbilityOp[F, Option[NonEmptyList[(Name[F], ArrowType)]]]
|
||||
|
||||
def defineService[F[_]](name: Ability[F], arrows: NonEmptyMap[String, ArrowType]): AbilityOp.Aux[F, Unit] =
|
||||
DefineService[F](name, arrows)
|
||||
}
|
||||
case class DefineService[F[_]](name: Ability[F], arrows: NonEmptyMap[String, ArrowType]) extends AbilityOp[F, Boolean]
|
||||
|
||||
case class DefineArrow[F[_]](arrow: Name[F], `type`: ArrowType) extends AbilityOp[Unit] {
|
||||
type CF[A] = F[A]
|
||||
}
|
||||
case class GetArrow[F[_]](name: Ability[F], arrow: Name[F]) extends AbilityOp[F, Option[ArrowType]]
|
||||
|
||||
case class PurgeArrows[F[_]]() extends AbilityOp[NonEmptyList[(Name[F], ArrowType)]] {
|
||||
type CF[A] = F[A]
|
||||
}
|
||||
case class SetServiceId[F[_]](name: Ability[F], id: Value[F]) extends AbilityOp[F, Boolean]
|
||||
|
||||
case class DefineService[F[_]](name: Ability[F], arrows: NonEmptyMap[String, ArrowType]) extends AbilityOp[Unit] {
|
||||
type CF[A] = F[A]
|
||||
}
|
||||
case class BeginScope[F[_]](token: Token[F]) extends AbilityOp[F, Unit]
|
||||
|
||||
case class GetArrow[F[_]](name: Ability[F], arrow: Name[F]) extends AbilityOp[ArrowType] {
|
||||
type CF[A] = F[A]
|
||||
}
|
||||
|
||||
case class SetServiceId[F[_]](name: Ability[F], id: Value[F]) extends AbilityOp[Unit] {
|
||||
type CF[A] = F[A]
|
||||
}
|
||||
|
||||
case class BeginScope[F[_]](token: Token[F]) extends AbilityOp[Unit] {
|
||||
type CF[A] = F[A]
|
||||
|
||||
def t: Token[CF] = token
|
||||
}
|
||||
|
||||
case class EndScope[F[_]]() extends AbilityOp[Unit] {
|
||||
type CF[A] = F[A]
|
||||
}
|
||||
case class EndScope[F[_]]() extends AbilityOp[F, Boolean]
|
||||
|
@ -3,34 +3,16 @@ package aqua.ast.algebra.names
|
||||
import aqua.ast.algebra.types.{ArrowType, Type}
|
||||
import aqua.parser.lexer.{Name, Token}
|
||||
|
||||
sealed trait NameOp[T] {
|
||||
type CF[_]
|
||||
}
|
||||
sealed trait NameOp[F[_], T]
|
||||
|
||||
object NameOp {
|
||||
type Aux[F[_], T] = NameOp[T] { type CF[A] = F[A] }
|
||||
}
|
||||
case class ReadName[F[_]](name: Name[F]) extends NameOp[F, Type]
|
||||
|
||||
case class ReadName[F[_]](name: Name[F]) extends NameOp[Type] {
|
||||
type CF[A] = F[A]
|
||||
}
|
||||
case class ReadArrow[F[_]](name: Name[F]) extends NameOp[F, Option[ArrowType]]
|
||||
|
||||
case class ReadArrow[F[_]](name: Name[F]) extends NameOp[ArrowType] {
|
||||
type CF[A] = F[A]
|
||||
}
|
||||
case class DefineName[F[_]](name: Name[F], `type`: Type) extends NameOp[F, Unit]
|
||||
|
||||
case class DefineName[F[_]](name: Name[F], `type`: Type) extends NameOp[Unit] {
|
||||
type CF[A] = F[A]
|
||||
}
|
||||
case class EraseName[F[_]](name: Name[F]) extends NameOp[F, Unit]
|
||||
|
||||
case class EraseName[F[_]](name: Name[F]) extends NameOp[Unit] {
|
||||
type CF[A] = F[A]
|
||||
}
|
||||
case class BeginScope[F[_]](token: Token[F]) extends NameOp[F, Unit]
|
||||
|
||||
case class BeginScope[F[_]](token: Token[F]) extends NameOp[Unit] {
|
||||
type CF[A] = F[A]
|
||||
}
|
||||
|
||||
case class EndScope[F[_]]() extends NameOp[Unit] {
|
||||
type CF[A] = F[A]
|
||||
}
|
||||
case class EndScope[F[_]]() extends NameOp[F, Unit]
|
||||
|
@ -5,30 +5,30 @@ import aqua.parser.lexer.{Name, Token}
|
||||
import cats.InjectK
|
||||
import cats.free.Free
|
||||
|
||||
class NamesAlgebra[F[_], Alg[_]](implicit V: InjectK[NameOp.Aux[F, *], Alg]) {
|
||||
class NamesAlgebra[F[_], Alg[_]](implicit V: InjectK[NameOp[F, *], Alg]) {
|
||||
|
||||
def read(name: Name[F]): Free[Alg, Type] =
|
||||
Free.liftInject[Alg](ReadName(name): NameOp.Aux[F, Type])
|
||||
Free.liftInject[Alg](ReadName(name))
|
||||
|
||||
def readArrow(name: Name[F]): Free[Alg, ArrowType] =
|
||||
Free.liftInject[Alg](ReadArrow(name): NameOp.Aux[F, ArrowType])
|
||||
def readArrow(name: Name[F]): Free[Alg, Option[ArrowType]] =
|
||||
Free.liftInject[Alg](ReadArrow(name))
|
||||
|
||||
def define(name: Name[F], `type`: Type): Free[Alg, Unit] =
|
||||
Free.liftInject[Alg](DefineName(name, `type`): NameOp.Aux[F, Unit])
|
||||
Free.liftInject[Alg](DefineName(name, `type`))
|
||||
|
||||
def erase(name: Name[F]): Free[Alg, Unit] =
|
||||
Free.liftInject[Alg](EraseName(name): NameOp.Aux[F, Unit])
|
||||
Free.liftInject[Alg](EraseName(name))
|
||||
|
||||
def beginScope(token: Token[F]): Free[Alg, Unit] =
|
||||
Free.liftInject[Alg](BeginScope(token): NameOp.Aux[F, Unit])
|
||||
Free.liftInject[Alg](BeginScope(token))
|
||||
|
||||
def endScope(): Free[Alg, Unit] =
|
||||
Free.liftInject[Alg](EndScope(): NameOp.Aux[F, Unit])
|
||||
Free.liftInject[Alg](EndScope[F]())
|
||||
|
||||
}
|
||||
|
||||
object NamesAlgebra {
|
||||
|
||||
implicit def namesAlgebra[F[_], Alg[_]](implicit V: InjectK[NameOp.Aux[F, *], Alg]): NamesAlgebra[F, Alg] =
|
||||
implicit def namesAlgebra[F[_], Alg[_]](implicit V: InjectK[NameOp[F, *], Alg]): NamesAlgebra[F, Alg] =
|
||||
new NamesAlgebra[F, Alg]()
|
||||
}
|
||||
|
@ -29,15 +29,19 @@ case class CoalgebraExpr[F[_]](
|
||||
): Prog[Alg, Gen] =
|
||||
ability
|
||||
.fold(N.readArrow(funcName))(A.getArrow(_, funcName))
|
||||
.flatMap(at =>
|
||||
V.checkArguments(at, args) >> variable
|
||||
.fold(Free.pure[Alg, Unit](()))(exportVar =>
|
||||
at.res.fold(
|
||||
// TODO: error! we're trying to export variable, but function has no export type
|
||||
Free.pure[Alg, Unit](())
|
||||
)(resType => N.define(exportVar, resType))
|
||||
)
|
||||
) as Gen("Coalgebra expression")
|
||||
.flatMap {
|
||||
case Some(at) =>
|
||||
V.checkArguments(at, args) >> variable
|
||||
.fold(Free.pure[Alg, Unit](()))(exportVar =>
|
||||
at.res.fold(
|
||||
// TODO: error! we're trying to export variable, but function has no export type
|
||||
Free.pure[Alg, Unit](())
|
||||
)(resType => N.define(exportVar, resType))
|
||||
// TODO: if it's a service, get service id, etc
|
||||
) as Gen("Coalgebra expression")
|
||||
case None =>
|
||||
Free.pure(Gen("Coalgebra expression errored"))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -28,15 +28,16 @@ case class ServiceExpr[F[_]](name: Ability[F], id: Option[Value[F]]) extends Exp
|
||||
Prog.around(
|
||||
A.beginScope(name),
|
||||
(_: Unit) =>
|
||||
(A.purgeArrows() <* A.endScope())
|
||||
.map(_.map(kv => kv._1.name.extract -> kv._2).toNem)
|
||||
.flatMap { arrows =>
|
||||
A.defineService(name, arrows) >>
|
||||
(A.purgeArrows(name) <* A.endScope()).flatMap {
|
||||
case Some(nel) =>
|
||||
A.defineService(name, nel.map(kv => kv._1.name.extract -> kv._2).toNem) >>
|
||||
id.fold(Free.pure[Alg, Gen](Gen.noop))(idV =>
|
||||
V.ensureIsString(idV) >> A.setServiceId(name, idV) as Gen.noop
|
||||
)
|
||||
case None =>
|
||||
Gen.noop.lift
|
||||
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user