ReturnExpr for functions

This commit is contained in:
dmitry 2021-03-17 13:10:21 +03:00
parent 35a3c4fc86
commit b4a9979278
13 changed files with 131 additions and 58 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

@ -5,10 +5,12 @@ service Local("local"):
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")
Local.onIn("Should be on root")
<- true

View File

@ -1,6 +1,6 @@
package aqua
import aqua.ast.{Ast, Gen}
import aqua.ast.{Ast, Compiler, Gen}
import cats.data.ValidatedNel
import aqua.parser.lift.Span

View File

@ -1,34 +1,21 @@
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.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 +39,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

@ -13,15 +13,14 @@ 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)
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 (_, y: FuncBodyGen) => y.copy(op = SeqGen(x, y.op))
case (_, y: ServiceCallGen) => SeqGen(x, y)
case (_, y: ParGen) => ParGen(Some(x), y.right)
case (x: AirGen, y: FuncBodyGen) => y.copy(op = SeqGen(x, y.op))
case (x: AirGen, y: ServiceCallGen) => SeqGen(x, y)
case (x: AirGen, y: ParGen) => ParGen(Some(x), y.right)
case (NoopGen, _) => y
case (_, NoopGen) => x
@ -30,24 +29,32 @@ object Gen {
println(Console.RED + s"drop x: ${x} in favor of y: $y" + Console.RESET)
y
}
}
}
def noop: Gen = NoopGen
def error: Gen = NoopGen
}
sealed trait AirGen extends Gen {
def last: AirGen = this
}
case object NoopGen extends Gen
case class SeqGen(left: Gen, right: Gen) extends Gen
case class SeqGen(left: AirGen, right: AirGen) extends AirGen {
override def last: AirGen = right
}
case class ServiceCallGen(peerId: String, serviceId: String, fnName: String, args: List[String], result: Option[String])
extends Gen
extends AirGen
case class FuncBodyGen(op: Gen) extends Gen
case class FuncBodyGen(op: AirGen) 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
case class ParGen(left: Option[AirGen], right: AirGen) extends AirGen {
override def last: AirGen = right
}

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,6 @@
package aqua.ast.expr
import aqua.ast.{Expr, Gen, NoopGen, Prog}
import aqua.ast.{Expr, Gen, Prog}
import aqua.ast.algebra.ValuesAlgebra
import aqua.ast.algebra.abilities.AbilitiesAlgebra
import aqua.parser.lexer.Token._

View File

@ -54,7 +54,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,14 +1,16 @@
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.{AirGen, Expr, FuncBodyGen, FuncGen, Gen, 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.parser.lexer.Token._
import aqua.parser.lexer.{Arg, DataTypeToken, Name}
import aqua.parser.lexer.{Arg, DataTypeToken, Name, Value}
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._
@ -16,11 +18,13 @@ import cats.{Applicative, Comonad}
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] =
@ -46,25 +50,57 @@ 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 =>
N.define(name, funcArrow, isRoot = true) as FuncGen(
name.value, // TODO: handle return value
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 l =>
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,6 +1,6 @@
package aqua.ast.expr
import aqua.ast.{Expr, Gen, ParGen, Prog}
import aqua.ast.{AirGen, Expr, Gen, ParGen, Prog}
import aqua.parser.lexer.Token._
import aqua.parser.lift.LiftParser
import aqua.parser.lift.LiftParser._
@ -10,7 +10,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,23 @@
package aqua.ast.expr
import aqua.ast.algebra.ValuesAlgebra
import aqua.ast.{Expr, Gen, 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

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