Basic AbilitiesInterpreter implementation

This commit is contained in:
dmitry 2021-03-12 17:29:51 +03:00
parent ef3fbec100
commit c403830d59
10 changed files with 146 additions and 132 deletions

View File

@ -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]

View File

@ -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")

View 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
}

View File

@ -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]()
}

View File

@ -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]]
)

View File

@ -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]

View File

@ -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]

View File

@ -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]()
}

View File

@ -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"))
}
}

View File

@ -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
}
}
)
}