Merge pull request #1 from fluencelabs/init-gen

First approach to generators
This commit is contained in:
Dmitry Kurinskiy 2021-03-18 12:11:56 +03:00 committed by GitHub
commit 0f6db200b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 547 additions and 162 deletions

View File

@ -15,7 +15,6 @@ lazy val root = project
"org.typelevel" %% "cats-effect" % "3.0.0-RC2",
"org.typelevel" %% "cats-parse" % "0.3.1",
"org.typelevel" %% "cats-free" % catsV,
"com.chuusai" %% "shapeless" % "2.3.3",
"com.github.julien-truffaut" %% "monocle-core" % monocleV,
"com.github.julien-truffaut" %% "monocle-macro" % monocleV
),

View File

@ -1,14 +1,27 @@
service Local("local"):
gt: u32, u32 -> bool
onIn: string -> ()
onBoolean: bool -> ()
func tryGen(in1: u32, in2: string) -> bool:
on in2:
Local.onIn(in2)
Local.gt(in1, 25)
v <- Local.gt(in1, 25)
<- v
func generateComplex(value: string) -> bool:
on "smth":
Local "other"
b <- tryGen(23, value)
Local.onIn("Should be on root")
t <- tryGen(24, "should be omitted")
Local.onIn("Should be on other")
Local.onIn("Should be on root")
Local.onBoolean(b)
<- b
func genArrows(arr: string -> bool):
arr("hi there")
--func wrapGenArrows():
-- on "some peer":
-- genArrows(generateComplex)

View File

@ -1,9 +1,12 @@
package aqua
import aqua.ast.{Ast, Gen}
import aqua.ast.gen.{Gen, ScriptGen}
import aqua.ast.{Ast, Compiler}
import cats.data.ValidatedNel
import aqua.parser.lift.Span
import scala.collection.immutable.Queue
object Aqua {
def parse(input: String): ValidatedNel[AquaError, Ast[Span.F]] =
@ -12,4 +15,9 @@ object Aqua {
def compile(input: String): ValidatedNel[AquaError, Gen] =
parse(input).andThen(ast => Compiler.compile(ast).leftMap(_.map(ts => CompilerError(ts._1.unit._1, ts._2))))
def generate(input: String): ValidatedNel[AquaError, Queue[String]] =
compile(input).map {
case g: ScriptGen => g.generateAir
case _ => Queue.empty
}
}

View File

@ -10,7 +10,7 @@ object Main extends IOApp.Simple {
override def run: IO[Unit] =
IO {
def process(str: String) =
Aqua.compile(str) match {
Aqua.generate(str) match {
case Validated.Valid(v)
println(v)
println(Console.GREEN + "Aqua script processed successfully" + Console.RESET)

View File

@ -1,34 +1,22 @@
package aqua
package aqua.ast
import aqua.ast.algebra.ReportError
import aqua.ast.expr.{
AbilityIdExpr,
AliasExpr,
ArrowTypeExpr,
CoalgebraExpr,
DataStructExpr,
FieldTypeExpr,
FuncExpr,
OnExpr,
ParExpr,
RootExpr,
ServiceExpr
}
import aqua.ast.algebra.abilities.{AbilitiesAlgebra, AbilitiesInterpreter, AbilitiesState, AbilityOp}
import aqua.ast.algebra.names.{NameOp, NamesAlgebra, NamesInterpreter, NamesState}
import aqua.ast.algebra.scope.{PeerIdAlgebra, PeerIdInterpreter, PeerIdOp, PeerIdState}
import aqua.ast.algebra.types.{TypeOp, TypesAlgebra, TypesInterpreter, TypesState}
import aqua.ast.{Ast, Expr, Gen, Prog}
import aqua.ast.expr._
import aqua.ast.gen.Gen
import aqua.parser.lexer.Token
import cats.Eval
import cats.arrow.FunctionK
import cats.data.Validated.{Invalid, Valid}
import cats.data.{EitherK, NonEmptyList, State, ValidatedNel}
import cats.Eval
import cats.free.Free
import cats.syntax.apply._
import cats.syntax.semigroup._
import monocle.Lens
import monocle.macros.GenLens
import cats.syntax.apply._
import cats.syntax.semigroup._
import scala.collection.immutable.Queue
@ -52,6 +40,7 @@ object Compiler {
case expr: FuncExpr[F] => expr.program[G]
case expr: OnExpr[F] => expr.program[G]
case expr: ParExpr[F] => expr.program[G]
case expr: ReturnExpr[F] => expr.program[G]
case expr: ServiceExpr[F] => expr.program[G]
case expr: RootExpr[F] => expr.program[G]
}

View File

@ -1,53 +0,0 @@
package aqua.ast
import cats.Semigroup
import cats.free.Free
import scala.collection.immutable.Queue
sealed trait Gen {
def lift[F[_]]: Free[F, Gen] = Free.pure(this)
}
object Gen {
implicit object GenSemigroup extends Semigroup[Gen] {
override def combine(x: Gen, y: Gen): Gen = {
println(Console.BLUE + x + " " + Console.YELLOW + y + Console.RESET)
(x, y) match {
case (x: ScriptGen, y: ScriptGen) => y.copy(funcs = y.funcs.enqueueAll(x.funcs))
case (x: FuncGen, y: FuncGen) => ScriptGen(Queue(x, y))
case (x: FuncGen, y: ScriptGen) => y.copy(funcs = y.funcs.enqueue(x))
case (_, y: FuncBodyGen) => y.copy(op = SeqGen(x, y.op))
case (_, y: ServiceCallGen) => SeqGen(x, y)
case (_, y: ParGen) => ParGen(Some(x), y.right)
case (NoopGen, _) => y
case (_, NoopGen) => x
case (_, y) =>
println(Console.RED + s"drop x: ${x} in favor of y: $y" + Console.RESET)
y
}
}
}
def noop: Gen = NoopGen
def error: Gen = NoopGen
}
case object NoopGen extends Gen
case class SeqGen(left: Gen, right: Gen) extends Gen
case class ServiceCallGen(peerId: String, serviceId: String, fnName: String, args: List[String], result: Option[String])
extends Gen
case class FuncBodyGen(op: Gen) extends Gen
case class FuncGen(name: String, body: FuncBodyGen) extends Gen
case class ScriptGen(funcs: Queue[FuncGen]) extends Gen
case class ParGen(left: Option[Gen], right: Gen) extends Gen

View File

@ -9,18 +9,32 @@ import cats.syntax.apply._
class ValuesAlgebra[F[_], Alg[_]](implicit N: NamesAlgebra[F, Alg], T: TypesAlgebra[F, Alg]) {
def ensureIsString(v: Value[F]): Free[Alg, Boolean] =
ensureTypeMatches(v, LiteralType.string)
def ensureTypeMatches(v: Value[F], expected: Type): Free[Alg, Boolean] =
resolveType(v).flatMap {
case Some(vt) =>
T.ensureTypeMatches(
v match {
case l: Literal[F] => l
case VarLambda(n, lambda) => lambda.lastOption.getOrElse(n)
},
expected,
vt
)
case None => Free.pure(false)
}
def resolveType(v: Value[F]): Free[Alg, Option[Type]] =
v match {
case l: Literal[F] =>
T.ensureTypeMatches(l, LiteralType.string, l.ts)
case v @ VarLambda(n, lambda) =>
Free pure [Alg, Option[Type]] Some(l.ts)
case VarLambda(n, lambda) =>
N.read(n).flatMap {
case Some(t) =>
T.resolveLambda(t, lambda).flatMap {
case Some(vt) => T.ensureTypeMatches(v.lambda.lastOption.getOrElse(n), LiteralType.string, vt)
case None => Free.pure(false)
}
T.resolveLambda(t, lambda)
case None =>
Free.pure(false)
Free.pure(None)
}
}

View File

@ -1,6 +1,7 @@
package aqua.ast.algebra.abilities
import aqua.ast.algebra.types.ArrowType
import aqua.ast.gen.ArrowGen
import aqua.parser.lexer.{Ability, Name, Token, Value}
import cats.InjectK
import cats.data.{NonEmptyList, NonEmptyMap}
@ -14,15 +15,18 @@ class AbilitiesAlgebra[F[_], Alg[_]](implicit A: InjectK[AbilityOp[F, *], Alg])
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, Boolean] =
def defineService(name: Ability[F], arrows: NonEmptyMap[String, ArrowGen]): Free[Alg, Boolean] =
Free.liftInject[Alg](DefineService[F](name, arrows))
def getArrow(name: Ability[F], arrow: Name[F]): Free[Alg, Option[ArrowType]] =
def getArrow(name: Ability[F], arrow: Name[F]): Free[Alg, Option[ArrowGen]] =
Free.liftInject[Alg](GetArrow[F](name, arrow))
def setServiceId(name: Ability[F], id: Value[F]): Free[Alg, Boolean] =
Free.liftInject[Alg](SetServiceId[F](name, id))
def getServiceId(name: String): Free[Alg, Option[Value[F]]] =
Free.liftInject[Alg](GetServiceId[F](name))
def beginScope(token: Token[F]): Free[Alg, Unit] =
Free.liftInject[Alg](BeginScope[F](token))

View File

@ -2,7 +2,8 @@ package aqua.ast.algebra.abilities
import aqua.ast.algebra.{ReportError, StackInterpreter}
import aqua.ast.algebra.types.ArrowType
import aqua.parser.lexer.{Name, Token, Value}
import aqua.ast.gen.ArrowGen
import aqua.parser.lexer.{Ability, Name, Token, Value}
import cats.data.{NonEmptyList, NonEmptyMap, State}
import cats.~>
import cats.syntax.functor._
@ -14,7 +15,7 @@ class AbilitiesInterpreter[F[_], X](implicit lens: Lens[X, AbilitiesState[F]], e
extends StackInterpreter[F, X, AbilitiesState[F], AbilityStackFrame[F]](GenLens[AbilitiesState[F]](_.stack))
with (AbilityOp[F, *] ~> State[X, *]) {
private def getService(name: String): S[Option[NonEmptyMap[String, ArrowType]]] =
private def getService(name: String): S[Option[NonEmptyMap[String, ArrowGen]]] =
getState.map(_.services.get(name))
override def apply[A](fa: AbilityOp[F, A]): State[X, A] =
@ -36,30 +37,46 @@ class AbilitiesInterpreter[F[_], X](implicit lens: Lens[X, AbilitiesState[F]], e
case ga: GetArrow[F] =>
getService(ga.name.value).flatMap {
case Some(arrows) =>
// TODO: must be resolved
arrows(ga.arrow.value)
.fold(
report(
ga.arrow,
s"Service found, but arrow is undefined, available: ${arrows.value.keys.toNonEmptyList.toList.mkString(", ")}"
).as(Option.empty[ArrowType])
).as(Option.empty[ArrowGen])
)(a => State.pure(Some(a)))
case None =>
report(ga.name, "Ability with this name is undefined").as(Option.empty[ArrowType])
report(ga.name, "Ability with this name is undefined").as(Option.empty[ArrowGen])
}
case s: SetServiceId[F] =>
getService(s.name.value).flatMap {
case Some(_) =>
mapStackHead(
modify(_.focus(_.rootServiceIds).index(s.name.value).replace(s.id)).as(true)
modify(st => st.copy(rootServiceIds = st.rootServiceIds.updated(s.name.value, s.id))).as(true)
)(h => h.copy(serviceIds = h.serviceIds.updated(s.name.value, s.id)) -> true)
case None =>
report(s.name, "Service with this name is not registered, can't set its ID").as(false)
}
case s: GetServiceId[F] =>
getState.flatMap(st =>
st.stack.flatMap(_.serviceIds.get(s.name)).headOption orElse st.rootServiceIds.get(s.name) match {
case None =>
st.stack.headOption
.map(_.token)
.fold(
// TODO this should be an impossible error
State.pure[X, Option[Value[F]]](Option.empty[Value[F]])
)(t =>
report(t, s"Service ID unresolved, use `${s.name} id` expression to set it")
.as(Option.empty[Value[F]])
)
case v => State.pure(v)
}
)
case da: DefineArrow[F] =>
mapStackHeadE(
report(da.arrow, "No abilities definition scope is found").as(false)
@ -82,7 +99,7 @@ class AbilitiesInterpreter[F[_], X](implicit lens: Lens[X, AbilitiesState[F]], e
case class AbilitiesState[F[_]](
stack: List[AbilityStackFrame[F]] = Nil,
services: Map[String, NonEmptyMap[String, ArrowType]] = Map.empty,
services: Map[String, NonEmptyMap[String, ArrowGen]] = Map.empty,
rootServiceIds: Map[String, Value[F]] = Map.empty[String, Value[F]]
) {

View File

@ -1,6 +1,7 @@
package aqua.ast.algebra.abilities
import aqua.ast.algebra.types.ArrowType
import aqua.ast.gen.ArrowGen
import aqua.parser.lexer.{Ability, Name, Token, Value}
import cats.data.{NonEmptyList, NonEmptyMap}
@ -10,12 +11,14 @@ case class DefineArrow[F[_]](arrow: Name[F], `type`: ArrowType) extends AbilityO
case class PurgeArrows[F[_]](token: Token[F]) extends AbilityOp[F, Option[NonEmptyList[(Name[F], ArrowType)]]]
case class DefineService[F[_]](name: Ability[F], arrows: NonEmptyMap[String, ArrowType]) extends AbilityOp[F, Boolean]
case class DefineService[F[_]](name: Ability[F], arrows: NonEmptyMap[String, ArrowGen]) extends AbilityOp[F, Boolean]
case class GetArrow[F[_]](name: Ability[F], arrow: Name[F]) extends AbilityOp[F, Option[ArrowType]]
case class GetArrow[F[_]](name: Ability[F], arrow: Name[F]) extends AbilityOp[F, Option[ArrowGen]]
case class SetServiceId[F[_]](name: Ability[F], id: Value[F]) extends AbilityOp[F, Boolean]
case class GetServiceId[F[_]](name: String) extends AbilityOp[F, Option[Value[F]]]
case class BeginScope[F[_]](token: Token[F]) extends AbilityOp[F, Unit]
case class EndScope[F[_]]() extends AbilityOp[F, Boolean]

View File

@ -1,15 +1,18 @@
package aqua.ast.algebra.names
import aqua.ast.algebra.types.{ArrowType, Type}
import aqua.ast.algebra.types.Type
import aqua.ast.gen.ArrowGen
import aqua.parser.lexer.{Name, Token}
sealed trait NameOp[F[_], T]
case class ReadName[F[_]](name: Name[F]) extends NameOp[F, Option[Type]]
case class ReadArrow[F[_]](name: Name[F]) extends NameOp[F, Option[ArrowType]]
case class ReadArrow[F[_]](name: Name[F]) extends NameOp[F, Option[ArrowGen]]
case class DefineName[F[_]](name: Name[F], `type`: Type, isRoot: Boolean) extends NameOp[F, Boolean]
case class DefineName[F[_]](name: Name[F], `type`: Type) extends NameOp[F, Boolean]
case class DefineArrow[F[_]](name: Name[F], gen: ArrowGen, isRoot: Boolean) extends NameOp[F, Boolean]
case class BeginScope[F[_]](token: Token[F]) extends NameOp[F, Unit]

View File

@ -1,6 +1,7 @@
package aqua.ast.algebra.names
import aqua.ast.algebra.types.{ArrowType, Type}
import aqua.ast.gen.ArrowGen
import aqua.parser.lexer.{Name, Token}
import cats.InjectK
import cats.free.Free
@ -10,11 +11,14 @@ class NamesAlgebra[F[_], Alg[_]](implicit V: InjectK[NameOp[F, *], Alg]) {
def read(name: Name[F]): Free[Alg, Option[Type]] =
Free.liftInject[Alg](ReadName(name))
def readArrow(name: Name[F]): Free[Alg, Option[ArrowType]] =
def readArrow(name: Name[F]): Free[Alg, Option[ArrowGen]] =
Free.liftInject[Alg](ReadArrow(name))
def define(name: Name[F], `type`: Type, isRoot: Boolean = false): Free[Alg, Boolean] =
Free.liftInject[Alg](DefineName(name, `type`, isRoot))
def define(name: Name[F], `type`: Type): Free[Alg, Boolean] =
Free.liftInject[Alg](DefineName(name, `type`))
def defineArrow(name: Name[F], gen: ArrowGen, isRoot: Boolean): Free[Alg, Boolean] =
Free.liftInject[Alg](DefineArrow(name, gen, isRoot))
def beginScope(token: Token[F]): Free[Alg, Unit] =
Free.liftInject[Alg](BeginScope(token))

View File

@ -1,7 +1,8 @@
package aqua.ast.algebra.names
import aqua.ast.algebra.types.{ArrowType, Type}
import aqua.ast.algebra.types.Type
import aqua.ast.algebra.{ReportError, StackInterpreter}
import aqua.ast.gen.ArrowGen
import aqua.parser.lexer.Token
import cats.data.State
import cats.~>
@ -18,7 +19,13 @@ class NamesInterpreter[F[_], X](implicit lens: Lens[X, NamesState[F]], error: Re
getState.map { st =>
st.stack.collectFirst {
case frame if frame.names.contains(name) => frame.names(name)
} orElse st.rootNames.get(name)
case frame if frame.arrows.contains(name) => frame.arrows(name).`type`
} orElse st.rootArrows.get(name).map(_.`type`)
}
def readArrow(name: String): S[Option[ArrowGen]] =
getState.map { st =>
st.stack.flatMap(_.arrows.get(name)).headOption orElse st.rootArrows.get(name)
}
override def apply[A](fa: NameOp[F, A]): State[X, A] =
@ -30,29 +37,36 @@ class NamesInterpreter[F[_], X](implicit lens: Lens[X, NamesState[F]], error: Re
getState.flatMap(st => report(rn.name, "Undefined name, available: " + st.allNames.mkString(", ")))
}
case ra: ReadArrow[F] =>
readName(ra.name.value).flatMap {
case Some(t: ArrowType) =>
State.pure(Option(t))
case Some(t) =>
report(ra.name, s"Arrow type expected, got: $t").as(Option.empty[ArrowType])
readArrow(ra.name.value).flatMap {
case Some(g) => State.pure(Option(g))
case None =>
getState.flatMap(st =>
report(ra.name, "Undefined name, available: " + st.allNames.mkString(", ")).as(Option.empty[ArrowType])
report(ra.name, "Undefined arrow, available: " + st.allNames.mkString(", "))
.as(Option.empty[ArrowGen])
)
}
case dn: DefineName[F] =>
readName(dn.name.value).flatMap {
case Some(_) => report(dn.name, "This name was already defined in the scope").as(false)
case None =>
mapStackHead(
if (dn.isRoot)
modify(st => st.copy(rootNames = st.rootNames.updated(dn.name.value, dn.`type`)))
report(dn.name, "Cannot define a variable in the root scope")
.as(false)
)(fr => fr.addName(dn.name.value, dn.`type`) -> true)
}
case da: DefineArrow[F] =>
readName(da.name.value).flatMap {
case Some(_) => report(da.name, "This name was already defined in the scope").as(false)
case None =>
mapStackHead(
if (da.isRoot)
modify(st => st.copy(rootArrows = st.rootArrows.updated(da.name.value, da.gen)))
.as(true)
else
report(dn.name, "Cannot define a variable in the root scope")
report(da.name, "Cannot define a variable in the root scope")
.as(false)
)(fr => fr.addName(dn.name.value, dn.`type`) -> true)
)(fr => fr.addArrow(da.name.value, da.gen) -> true)
}
case bs: BeginScope[F] =>
beginScope(NamesFrame(bs.token))
@ -61,10 +75,20 @@ class NamesInterpreter[F[_], X](implicit lens: Lens[X, NamesState[F]], error: Re
}).asInstanceOf[State[X, A]]
}
case class NamesState[F[_]](stack: List[NamesFrame[F]] = Nil, rootNames: Map[String, Type] = Map.empty) {
def allNames: LazyList[String] = LazyList.from(stack).flatMap(_.names.keys).appendedAll(rootNames.keys)
case class NamesState[F[_]](stack: List[NamesFrame[F]] = Nil, rootArrows: Map[String, ArrowGen] = Map.empty) {
def allNames: LazyList[String] =
LazyList.from(stack).flatMap(s => s.names.keys ++ s.arrows.keys).appendedAll(rootArrows.keys)
def allArrows: LazyList[String] =
LazyList.from(stack).flatMap(_.arrows.keys).appendedAll(rootArrows.keys)
}
case class NamesFrame[F[_]](token: Token[F], names: Map[String, Type] = Map.empty) {
case class NamesFrame[F[_]](
token: Token[F],
names: Map[String, Type] = Map.empty,
arrows: Map[String, ArrowGen] = Map.empty
) {
def addName(n: String, t: Type): NamesFrame[F] = copy[F](names = names.updated(n, t))
def addArrow(n: String, g: ArrowGen): NamesFrame[F] = copy[F](arrows = arrows.updated(n, g))
}

View File

@ -1,8 +1,9 @@
package aqua.ast.expr
import aqua.ast.{Expr, Gen, NoopGen, Prog}
import aqua.ast.{Expr, Prog}
import aqua.ast.algebra.ValuesAlgebra
import aqua.ast.algebra.abilities.AbilitiesAlgebra
import aqua.ast.gen.Gen
import aqua.parser.lexer.Token._
import aqua.parser.lexer.{Ability, Value}
import aqua.parser.lift.LiftParser

View File

@ -1,7 +1,8 @@
package aqua.ast.expr
import aqua.ast.{Expr, Gen, Prog}
import aqua.ast.{Expr, Prog}
import aqua.ast.algebra.types.TypesAlgebra
import aqua.ast.gen.Gen
import aqua.parser.lexer.Token._
import aqua.parser.lexer.{CustomTypeToken, TypeToken}
import aqua.parser.lift.LiftParser

View File

@ -1,8 +1,9 @@
package aqua.ast.expr
import aqua.ast.{Expr, Gen, Prog}
import aqua.ast.{Expr, Prog}
import aqua.ast.algebra.abilities.AbilitiesAlgebra
import aqua.ast.algebra.types.TypesAlgebra
import aqua.ast.gen.Gen
import aqua.parser.lexer.Token._
import aqua.parser.lexer.{ArrowTypeToken, Name}
import aqua.parser.lift.LiftParser

View File

@ -1,10 +1,11 @@
package aqua.ast.expr
import aqua.ast.{Expr, Gen, Prog, ServiceCallGen}
import aqua.ast.{Expr, Prog}
import aqua.ast.algebra.ValuesAlgebra
import aqua.ast.algebra.abilities.AbilitiesAlgebra
import aqua.ast.algebra.names.NamesAlgebra
import aqua.ast.algebra.types.TypesAlgebra
import aqua.ast.gen.{Gen, ServiceCallGen}
import aqua.parser.lexer.Token._
import aqua.parser.lexer.{Ability, Name, Value}
import aqua.parser.lift.LiftParser
@ -31,20 +32,13 @@ case class CoalgebraExpr[F[_]](
.fold(N.readArrow(funcName))(A.getArrow(_, funcName))
.flatMap {
case Some(at) =>
V.checkArguments(at, args) >> variable
V.checkArguments(at.`type`, args) >> variable
.fold(Free.pure[Alg, Boolean](true))(exportVar =>
at.res.fold(
at.`type`.res.fold(
// TODO: error! we're trying to export variable, but function has no export type
Free.pure[Alg, Boolean](false)
)(resType => N.define(exportVar, resType))
// TODO: if it's a service, get service id, etc
) as (ServiceCallGen(
peerId = "peerId",
serviceId = "#" + ability.map(_.value).getOrElse("???"),
fnName = funcName.value,
args = args.map(_.toString),
result = variable.map(_.value)
): Gen)
) >> at.gen[F, Alg](args, variable).widen[Gen]
case None =>
Gen.error.lift[Alg]
}
@ -54,7 +48,7 @@ case class CoalgebraExpr[F[_]](
object CoalgebraExpr extends Expr.Leaf {
override def p[F[_]: LiftParser: Comonad]: P[CoalgebraExpr[F]] =
((Name.p[F] <* `<-`).backtrack.?.with1 ~
((Name.p[F] <* ` <- `).backtrack.?.with1 ~
((Ability.ab[F] <* `.`).?.with1 ~
Name.p[F] ~
comma0(Value.`value`[F]).between(`(`, `)`))).map {

View File

@ -1,8 +1,9 @@
package aqua.ast.expr
import aqua.ast.{Expr, Gen, Prog}
import aqua.ast.{Expr, Prog}
import aqua.ast.algebra.names.NamesAlgebra
import aqua.ast.algebra.types.TypesAlgebra
import aqua.ast.gen.Gen
import aqua.parser.lexer.CustomTypeToken
import aqua.parser.lexer.Token._
import aqua.parser.lift.LiftParser

View File

@ -1,7 +1,8 @@
package aqua.ast.expr
import aqua.ast.{Expr, Gen, Prog}
import aqua.ast.{Expr, Prog}
import aqua.ast.algebra.types.TypesAlgebra
import aqua.ast.gen.Gen
import aqua.parser.lexer.Token._
import aqua.parser.lexer.{DataTypeToken, Name}
import aqua.parser.lift.LiftParser

View File

@ -1,26 +1,32 @@
package aqua.ast.expr
import aqua.ast.{Expr, FuncBodyGen, FuncGen, Gen, Prog}
import aqua.ast.Ast.Tree
import aqua.ast.algebra.ValuesAlgebra
import aqua.ast.{Expr, Indent, Prog}
import aqua.ast.algebra.abilities.AbilitiesAlgebra
import aqua.ast.algebra.names.NamesAlgebra
import aqua.ast.algebra.scope.PeerIdAlgebra
import aqua.ast.algebra.types.{ArrowType, Type, TypesAlgebra}
import aqua.ast.algebra.types.{ArrowType, DataType, Type, TypesAlgebra}
import aqua.ast.gen.DataView.InitPeerId
import aqua.ast.gen.{AirContext, AirGen, ArrowGen, DataView, FuncBodyGen, FuncGen, Gen}
import aqua.parser.lexer.Token._
import aqua.parser.lexer.{Arg, DataTypeToken, Name}
import aqua.parser.lexer.{Arg, DataTypeToken, Name, Value, VarLambda}
import aqua.parser.lift.LiftParser
import cats.free.Free
import cats.free.{Cofree, Free}
import cats.parse.Parser
import cats.syntax.flatMap._
import cats.syntax.functor._
import cats.{Applicative, Comonad}
import cats.{Applicative, Comonad, Eval}
import scala.collection.immutable.Queue
case class FuncExpr[F[_]](name: Name[F], args: List[Arg[F]], ret: Option[DataTypeToken[F]]) extends Expr[F] {
case class FuncExpr[F[_]](name: Name[F], args: List[Arg[F]], ret: Option[DataTypeToken[F]], retValue: Option[Value[F]])
extends Expr[F] {
def program[Alg[_]](implicit
T: TypesAlgebra[F, Alg],
N: NamesAlgebra[F, Alg],
V: ValuesAlgebra[F, Alg],
P: PeerIdAlgebra[F, Alg],
A: AbilitiesAlgebra[F, Alg]
): Prog[Alg, Gen] =
@ -37,6 +43,8 @@ case class FuncExpr[F[_]](name: Name[F], args: List[Arg[F]], ret: Option[DataTyp
// Resolve arg type, remember it
f.flatMap(acc =>
T.resolveType(argType).flatMap {
case Some(t: ArrowType) =>
N.defineArrow(argName, ArrowGen.arg(argName.value, t), isRoot = false).as(acc.enqueue(t))
case Some(t) =>
N.define(argName, t).as(acc.enqueue(t))
case None =>
@ -46,25 +54,84 @@ case class FuncExpr[F[_]](name: Name[F], args: List[Arg[F]], ret: Option[DataTyp
}
.map(_.toList),
// Resolve return type
// TODO handle return VALUE!
ret.fold(Free.pure[Alg, Option[Type]](None))(T.resolveType(_))
)
.map(argsAndRes => ArrowType(argsAndRes._1, argsAndRes._2)),
(funcArrow: ArrowType, bodyGen: Gen) =>
// Check return value type
((funcArrow.res, retValue) match {
case (Some(t), Some(v)) =>
V.resolveType(v).flatMap {
case Some(vt) => T.ensureTypeMatches(v, t, vt).void
case None => Free.pure[Alg, Unit](())
}
case _ =>
Free.pure[Alg, Unit](())
})
// Erase arguments and internal variables
A.endScope() >> N.endScope() >> N.define(name, funcArrow, isRoot = true) as FuncGen(
name.value,
FuncBodyGen(bodyGen)
)
>> A.endScope() >> N.endScope() >> (bodyGen match {
case bg: AirGen if ret.isDefined == retValue.isDefined =>
val argNames = args.map(_.name.value)
N.defineArrow(
name,
ArrowGen.func(funcArrow, argNames, retValue.map(ArrowGen.valueToData), FuncBodyGen(bg)),
isRoot = true
) as FuncGen(
name.value,
Eval.later {
bg.generate(
AirContext(
data = argNames
.zip(funcArrow.args)
.collect { //TODO preload these variables
case (an, _: DataType) =>
an -> DataView.Variable(an)
}
.toMap,
arrows = argNames
.zip(funcArrow.args)
.collect {
case (an, _: ArrowType) =>
an -> new ArrowGen.SrvCallableOnPeer(InitPeerId, DataView.StringScalar("callback"), an)
}
.toMap,
vars = argNames.toSet
)
)
._2
},
FuncBodyGen(bg)
)
case _ => Gen.noop.lift
})
)
}
object FuncExpr extends Expr.AndIndented(OnExpr, AbilityIdExpr, CoalgebraExpr, ParExpr) {
object FuncExpr extends Expr.AndIndented(OnExpr, AbilityIdExpr, ReturnExpr, CoalgebraExpr, ParExpr) {
override def p[F[_]: LiftParser: Comonad]: Parser[FuncExpr[F]] =
((`func` *> ` ` *> Name.p[F]) ~ comma0(Arg.p)
.between(`(`, `)`) ~ (`->` *> DataTypeToken.`datatypedef`).? <* ` : \n+`).map {
case ((name, args), ret) => FuncExpr(name, args, ret)
.between(`(`, `)`) ~ (` -> ` *> DataTypeToken.`datatypedef`).? <* ` : \n+`).map {
case ((name, args), ret) => FuncExpr(name, args, ret, None)
}
override def ast[F[_]: LiftParser: Comonad](ps: Indent): Parser[Tree[F]] =
super.ast(ps).flatMap { tree =>
tree.head match {
case funcExpr: FuncExpr[F] if funcExpr.ret.isDefined =>
tree.tail.value.lastOption.map(_.head) match {
case Some(re: ReturnExpr[F]) =>
Parser.pure(
Cofree(funcExpr.copy(retValue = Some(re.value)), tree.tail)
)
case _ =>
Parser.failWith(
"Return type is defined for function, but nothing returned. Use `<- value` as the last expression inside function body."
)
}
case _ => Parser.pure(tree)
}
}
}

View File

@ -1,9 +1,10 @@
package aqua.ast.expr
import aqua.ast.{Expr, Gen, Prog}
import aqua.ast.{Expr, Prog}
import aqua.ast.algebra.ValuesAlgebra
import aqua.ast.algebra.abilities.AbilitiesAlgebra
import aqua.ast.algebra.scope.PeerIdAlgebra
import aqua.ast.gen.{AirGen, ArrowGen, Gen}
import aqua.parser.lexer.Token._
import aqua.parser.lexer.Value
import aqua.parser.lift.LiftParser
@ -21,7 +22,12 @@ case class OnExpr[F[_]](peerId: Value[F]) extends Expr[F] {
): Prog[Alg, Gen] =
Prog.around(
V.ensureIsString(peerId) >> P.onPeerId(peerId) >> A.beginScope(peerId),
(_: Unit, ops: Gen) => A.endScope() >> P.erasePeerId() as ops
(_: Unit, ops: Gen) =>
A.endScope() >> P.erasePeerId() as (ops match {
case air: AirGen =>
air.wrap(c => (c.copy(peerId = ArrowGen.valueToData(peerId)), _.copy(peerId = c.peerId)))
case _ => ops
})
)
}

View File

@ -1,6 +1,7 @@
package aqua.ast.expr
import aqua.ast.{Expr, Gen, ParGen, Prog}
import aqua.ast.gen.{AirGen, Gen, ParGen}
import aqua.ast.{Expr, Prog}
import aqua.parser.lexer.Token._
import aqua.parser.lift.LiftParser
import aqua.parser.lift.LiftParser._
@ -10,7 +11,10 @@ import cats.parse.Parser
case class ParExpr[F[_]](point: F[Unit]) extends Expr[F] {
def program[Alg[_]]: Prog[Alg, Gen] =
Prog.after[Alg, Gen](g => ParGen(left = None, right = g).lift)
Prog.after[Alg, Gen] {
case g: AirGen => ParGen(left = None, right = g).lift
case g => g.lift
}
}
object ParExpr extends Expr.AndThen(OnExpr, CoalgebraExpr) {

View File

@ -0,0 +1,24 @@
package aqua.ast.expr
import aqua.ast.algebra.ValuesAlgebra
import aqua.ast.gen.Gen
import aqua.ast.{Expr, Prog}
import aqua.parser.lexer.Token._
import aqua.parser.lexer.Value
import aqua.parser.lift.LiftParser
import cats.Comonad
import cats.parse.Parser
import cats.syntax.functor._
case class ReturnExpr[F[_]](value: Value[F]) extends Expr[F] {
def program[Alg[_]](implicit V: ValuesAlgebra[F, Alg]): Prog[Alg, Gen] =
V.resolveType(value).as(Gen.noop)
}
object ReturnExpr extends Expr.Leaf {
override def p[F[_]: LiftParser: Comonad]: Parser[Expr[F]] =
(`<-` *> ` ` *> Value.`value`[F]).map(ReturnExpr(_))
}

View File

@ -1,6 +1,7 @@
package aqua.ast.expr
import aqua.ast.{Expr, Gen, Prog, ScriptGen}
import aqua.ast.gen.{Gen, ScriptGen}
import aqua.ast.{Expr, Prog}
import cats.syntax.semigroup._
import scala.collection.immutable.Queue

View File

@ -1,10 +1,11 @@
package aqua.ast.expr
import aqua.ast.{Expr, Gen, Prog}
import aqua.ast.{Expr, Prog}
import aqua.ast.algebra.ValuesAlgebra
import aqua.ast.algebra.abilities.AbilitiesAlgebra
import aqua.ast.algebra.names.NamesAlgebra
import aqua.ast.algebra.types.TypesAlgebra
import aqua.ast.gen.{ArrowGen, Gen}
import aqua.parser.lexer.Token._
import aqua.parser.lexer.{Ability, Value}
import aqua.parser.lift.LiftParser
@ -28,7 +29,10 @@ case class ServiceExpr[F[_]](name: Ability[F], id: Option[Value[F]]) extends Exp
(_: Unit, body: Gen) =>
(A.purgeArrows(name) <* A.endScope()).flatMap {
case Some(nel) =>
A.defineService(name, nel.map(kv => kv._1.value -> kv._2).toNem) >>
A.defineService(
name,
nel.map(kv => kv._1.value -> ArrowGen.service(name.value, kv._1.value, kv._2)).toNem
) >>
id.fold(Free.pure[Alg, Gen](Gen.noop))(idV =>
V.ensureIsString(idV) >> A.setServiceId(name, idV) as Gen.noop
)

View File

@ -1,4 +1,4 @@
package aqua.ir
package aqua.ast.gen
import cats.Show
import cats.syntax.show._
@ -41,7 +41,9 @@ object DataView {
case class Stream(name: String) extends DataView
case class VarLens(name: String, lens: String) extends DataView
case class VarLens(name: String, lens: String) extends DataView {
def append(sublens: String): VarLens = copy(lens = lens + sublens)
}
implicit val show: Show[DataView] = Show.show {
case StringScalar(v) v
@ -49,7 +51,7 @@ object DataView {
case LastError "%last_error%"
case Variable(name) name
case Stream(name) name
case VarLens(name, lens) s"$name.$lens"
case VarLens(name, lens) name + ".$" + lens
}
}
@ -63,7 +65,7 @@ object Triplet {
implicit val show: Show[Triplet] = Show.show {
case FromData(ps, fn) s"${ps.show} " + "\"" + fn + "\""
case Full(p, s, fn) s"${p.show} (${s.show} " + "\"" + fn + "\""
case Full(p, s, fn) s"${p.show} (${s.show} " + "\"" + fn + "\")"
}
}

View File

@ -0,0 +1,21 @@
package aqua.ast.gen
import DataView.InitPeerId
case class AirContext(
data: Map[String, DataView] = Map.empty,
arrows: Map[String, ArrowGen.Callable] = Map.empty,
peerId: DataView = InitPeerId,
vars: Set[String] = Set.empty,
instrCounter: Int = 0
) {
def mergePar(other: AirContext): AirContext =
copy(
instrCounter = instrCounter max other.instrCounter,
data = data ++ other.data,
vars = vars ++ other.vars
)
def incr: AirContext = copy(instrCounter = instrCounter + 1)
}

View File

@ -0,0 +1,104 @@
package aqua.ast.gen
import aqua.ast.algebra.abilities.AbilitiesAlgebra
import aqua.ast.algebra.types.ArrowType
import DataView.InitPeerId
import aqua.parser.lexer.{IntoArray, IntoField, LambdaOp, Literal, Name, Value, VarLambda}
import cats.free.Free
abstract class ArrowGen(val `type`: ArrowType) {
def gen[F[_], Alg[_]](args: List[Value[F]], result: Option[Name[F]])(implicit
A: AbilitiesAlgebra[F, Alg]
): Free[Alg, AirGen]
}
object ArrowGen {
private def opsToLens[F[_]](ops: List[LambdaOp[F]]): String =
ops match {
case Nil => ""
case (_: IntoArray[F]) :: tail => "[@" + opsToLens(tail) + "]"
case (f: IntoField[F]) :: tail => "." + f.value + opsToLens(tail)
}
def valueToData[F[_]](v: Value[F]): DataView =
v match {
case l: Literal[F] => DataView.StringScalar(l.value)
case VarLambda(name, Nil) => DataView.Variable(name.value)
case VarLambda(name, ops) => DataView.VarLens(name.value, opsToLens(ops))
}
private def argsToData[F[_]](args: List[Value[F]]): List[DataView] = args.map(valueToData)
trait Callable {
def toCallGen(args: List[DataView], result: Option[String]): AirGen
}
class FuncCallable(argNames: List[String], retValue: Option[DataView], bodyGen: FuncBodyGen) extends Callable {
override def toCallGen(args: List[DataView], result: Option[String]): AirGen =
bodyGen.op
.wrap(c =>
(
c.copy(data = c.data ++ argNames.zip(args)),
_.copy(data = c.data ++ result.zip(retValue))
)
)
}
class SrvCallable(srvId: DataView, fnName: String) extends Callable {
override def toCallGen(args: List[DataView], result: Option[String]): AirGen =
ServiceCallGen(srvId, fnName, args, result)
}
class SrvCallableOnPeer(peerId: DataView, srvId: DataView, fnName: String) extends Callable {
override def toCallGen(args: List[DataView], result: Option[String]): AirGen =
ServiceCallGen(srvId, fnName, args, result).wrap(ctx => (ctx.copy(peerId = peerId), _.copy(peerId = ctx.peerId)))
}
def func(`type`: ArrowType, argNames: List[String], retValue: Option[DataView], bodyGen: FuncBodyGen): ArrowGen =
new ArrowGen(`type`) {
override def gen[F[_], Alg[_]](args: List[Value[F]], result: Option[Name[F]])(implicit
A: AbilitiesAlgebra[F, Alg]
): Free[Alg, AirGen] =
Free.pure[Alg, AirGen](
new FuncCallable(argNames, retValue, bodyGen).toCallGen(argsToData(args), result.map(_.value))
)
}
def service(name: String, fnName: String, `type`: ArrowType): ArrowGen =
new ArrowGen(`type`) {
override def gen[F[_], Alg[_]](args: List[Value[F]], result: Option[Name[F]])(implicit
A: AbilitiesAlgebra[F, Alg]
): Free[Alg, AirGen] =
// TODO it's really weird that we're losing token here
A.getServiceId(name).map {
case Some(sid) =>
new SrvCallable(valueToData(sid), fnName).toCallGen(argsToData(args), result.map(_.value))
case None =>
NullGen
}
}
def arg(name: String, `type`: ArrowType): ArrowGen =
new ArrowGen(`type`) {
override def gen[F[_], Alg[_]](args: List[Value[F]], result: Option[Name[F]])(implicit
A: AbilitiesAlgebra[F, Alg]
): Free[Alg, AirGen] =
Free.pure[Alg, AirGen](
new AirGen {
override def generate(ctx: AirContext): (AirContext, Air) = {
println(Console.YELLOW + ctx + Console.RESET)
ctx.arrows(name).toCallGen(argsToData(args), result.map(_.value)).generate(ctx)
}
}
)
}
}

View File

@ -0,0 +1,127 @@
package aqua.ast.gen
import cats.{Eval, Semigroup}
import cats.free.Free
import cats.syntax.functor._
import cats.syntax.show._
import scala.collection.immutable.Queue
sealed trait Gen {
def lift[F[_]]: Free[F, Gen] = Free.pure(this)
}
object Gen {
implicit object GenSemigroup extends Semigroup[Gen] {
override def combine(x: Gen, y: Gen): Gen =
(x, y) match {
case (x: ScriptGen, y: ScriptGen) => y.copy(funcs = y.funcs.enqueueAll(x.funcs))
case (x: FuncGen, y: FuncGen) => ScriptGen(Queue(x, y))
case (x: FuncGen, y: ScriptGen) => y.copy(funcs = y.funcs.enqueue(x))
case (x: ScriptGen, y: FuncGen) => x.copy(funcs = x.funcs.enqueue(y))
case (x: AirGen, y: FuncBodyGen) => y.copy(op = SeqGen(x, y.op))
case (x: AirGen, y: ParGen) => ParGen(Some(x), y.right)
case (x: AirGen, y: AirGen) => SeqGen(x, y)
case (NoopGen, _) => y
case (_, NoopGen) => x
case (_, y) =>
println(Console.RED + s"drop x: ${x} in favor of y: $y" + Console.RESET)
y
}
}
def noop: Gen = NoopGen
def error: Gen = NoopGen
}
trait AirGen extends Gen {
self =>
def generate(ctx: AirContext): (AirContext, Air)
def wrap(f: AirContext => (AirContext, AirContext => AirContext)): AirGen =
new AirGen {
override def generate(ctx: AirContext): (AirContext, Air) = {
val (setup, clean) = f(ctx)
val (internal, res) = self.generate(setup)
(clean(internal), res)
}
}
}
case object NullGen extends AirGen {
override def generate(ctx: AirContext): (AirContext, Air) = (ctx, Air.Null)
}
case object NoopGen extends Gen
case class SeqGen(left: AirGen, right: AirGen) extends AirGen {
override def generate(ctx: AirContext): (AirContext, Air) = {
println(Console.BLUE + ctx + Console.RESET)
val (c, l) = left.generate(ctx)
right.generate(c).swap.map(_.incr).swap.map(Air.Seq(l, _))
}
}
case class ServiceCallGen(
srvId: DataView,
fnName: String,
args: List[DataView],
result: Option[String]
) extends AirGen {
override def generate(ctx: AirContext): (AirContext, Air) = {
val (c, res) = result.fold(ctx -> Option.empty[String]) {
case r if ctx.vars(r) =>
val vn = r + ctx.instrCounter
ctx.copy(vars = ctx.vars + vn, data = ctx.data.updated(r, DataView.Variable(vn))) -> Option(vn)
case r =>
ctx.copy(vars = ctx.vars + r, data = ctx.data.updated(r, DataView.Variable(r))) -> Option(r)
}
c.incr -> Air.Call(
Triplet.Full(ctx.peerId, srvId, fnName),
args.map {
case DataView.Variable(name) => ctx.data(name)
case DataView.Stream(name) => ctx.data(name)
case DataView.VarLens(name, lens) =>
ctx.data(name) match {
case DataView.Variable(n) => DataView.VarLens(n, lens)
case DataView.Stream(n) => DataView.VarLens(n, lens)
case vl: DataView.VarLens => vl.append(lens)
case a => a // actually, it's an error
}
case a => a
},
res
)
}
}
case class FuncBodyGen(op: AirGen) extends Gen
case class FuncGen(name: String, air: Eval[Air], body: FuncBodyGen) extends Gen {
def generateAir: Air = air.memoize.value
}
case class ScriptGen(funcs: Queue[FuncGen]) extends Gen {
def generateAir: Queue[String] =
funcs.map(_.generateAir.show)
}
case class ParGen(left: Option[AirGen], right: AirGen) extends AirGen {
override def generate(ctx: AirContext): (AirContext, Air) =
left.fold(right.generate(ctx)) { l =>
val (lc, la) = l.generate(ctx)
val (rc, ra) = right.generate(ctx.incr)
(lc.mergePar(rc).incr, Air.Par(la, ra))
}
}

View File

@ -41,8 +41,9 @@ object Token {
val `*` : P[Unit] = P.char('*')
val `(` : P[Unit] = ` `.?.with1 *> P.char('(') <* ` `.?
val `)` : P[Unit] = ` `.?.with1 *> P.char(')') <* ` `.?
val `->` : P[Unit] = ` `.?.with1 *> P.string("->") <* ` `.?
val `<-` : P[Unit] = (` `.?.with1 *> P.string("<-") <* ` `.?).backtrack
val ` -> ` : P[Unit] = ` `.?.with1 *> P.string("->") <* ` `.?
val ` <- ` : P[Unit] = (` `.?.with1 *> P.string("<-") <* ` `.?).backtrack
val `<-` : P[Unit] = P.string("<-").backtrack
def comma[T](p: P[T]): P[NonEmptyList[T]] =
P.repSep(p, `,` <* ` \n+`.rep0)

View File

@ -62,7 +62,7 @@ case class ArrowTypeToken[F[_]: Comonad](
object ArrowTypeToken {
def `arrowdef`[F[_]: LiftParser: Comonad]: P[ArrowTypeToken[F]] =
(comma0(DataTypeToken.`datatypedef`).with1 ~ `->`.lift ~
(comma0(DataTypeToken.`datatypedef`).with1 ~ ` -> `.lift ~
(DataTypeToken.`datatypedef`
.map(Some(_)) | P.string("()").as(None))).map {
case ((args, point), res) ArrowTypeToken(point, args, res)