AssignmentExpression (#155)

* AssignmentExpression

* Increment minor version due to syntax enhancement

* Don't generate (null) for assignment tags

* AbilityIdTag

* Added defaultId to ServiceModel

* fix compilation

Co-authored-by: DieMyst <dmitry.shakhtarin@fluence.ai>
This commit is contained in:
Dmitry Kurinskiy 2021-06-08 09:42:09 +03:00 committed by GitHub
parent 15f582eaf9
commit d300a7dea3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 163 additions and 53 deletions

View File

@ -3,8 +3,18 @@ service OpH("op"):
func a(b: string) -> string:
c <- OpH.puk(b)
e = "hello"
OpH.puk(e)
f = "world"
OpH "planet"
OpH.puk(f)
x = "!!!"
OpH.puk(x)
<- c
func d(e: string) -> string:
f <- a(e)
OpH.puk("in func d(e) now")
<- f

View File

@ -3,12 +3,11 @@ package aqua.backend.air
import aqua.model._
import aqua.model.func.Call
import aqua.model.func.body._
import aqua.types.{OptionType, StreamType}
import aqua.types.StreamType
import cats.Eval
import cats.data.Chain
import cats.free.Cofree
import scala.annotation.tailrec
import wvlet.log.Logger
sealed trait AirGen {
def generate: Air
@ -17,6 +16,9 @@ sealed trait AirGen {
object AirGen {
private val logger = Logger.of[AirGen.type]
import logger._
def lambdaToString(ls: List[LambdaModel]): String = ls match {
case Nil => ""
case IntoArrayModel(_) :: tail =>
@ -85,8 +87,8 @@ object AirGen {
case CallArrowTag(funcName, _) =>
// TODO: should be already resolved & removed from tree
println(
Console.RED + s"Unresolved arrow in AirGen: $funcName" + Console.RESET
error(
s"Unresolved arrow in AirGen: $funcName"
)
Eval later NullGen
@ -95,10 +97,13 @@ object AirGen {
Eval later opsToSingle(
ops
)
case _: NoAirTag =>
// TODO: should be already resolved & removed from tree
Eval later NullGen
case XorParTag(opsx, opsy) =>
// TODO should be resolved
println(
Console.RED + "XorParTag reached AirGen, most likely it's an error" + Console.RESET
error(
"XorParTag reached AirGen, most likely it's an error"
)
Eval later opsToSingle(
Chain(apply(opsx.tree), apply(opsy.tree))

View File

@ -24,12 +24,13 @@ val airframeLog = "org.wvlet.airframe" %% "airframe-log" % airframeLogV
name := "aqua-hll"
val commons = Seq(
baseAquaVersion := "0.1.4",
baseAquaVersion := "0.1.5",
version := baseAquaVersion.value + "-" + sys.env.getOrElse("BUILD_NUMBER", "SNAPSHOT"),
scalaVersion := dottyVersion,
libraryDependencies ++= Seq(
"org.typelevel" %% "log4cats-core" % log4catsV,
"org.scalatest" %% "scalatest" % scalaTestV % Test
airframeLog,
"org.scalatest" %% "scalatest" % scalaTestV % Test
),
addCompilerPlugin("org.typelevel" %% "kind-projector" % "0.11.3" cross CrossVersion.full)
)
@ -43,16 +44,15 @@ lazy val cli = project
assembly / mainClass := Some("aqua.AquaCli"),
assembly / assemblyJarName := "aqua-cli-" + version.value + ".jar",
libraryDependencies ++= Seq(
"com.monovore" %% "decline" % declineV,
"com.monovore" %% "decline-effect" % declineV,
"org.typelevel" %% "cats-effect" % catsEffectV,
"co.fs2" %% "fs2-core" % fs2V,
"co.fs2" %% "fs2-io" % fs2V,
"org.typelevel" %% "log4cats-slf4j" % log4catsV,
airframeLog,
"com.beachape" %% "enumeratum" % enumeratumV,
"org.slf4j" % "slf4j-jdk14" % slf4jV,
"com.monovore" %% "decline-enumeratum" % declineEnumV
"com.monovore" %% "decline" % declineV,
"com.monovore" %% "decline-effect" % declineV,
"org.typelevel" %% "cats-effect" % catsEffectV,
"co.fs2" %% "fs2-core" % fs2V,
"co.fs2" %% "fs2-io" % fs2V,
"org.typelevel" %% "log4cats-slf4j" % log4catsV,
"com.beachape" %% "enumeratum" % enumeratumV,
"org.slf4j" % "slf4j-jdk14" % slf4jV,
"com.monovore" %% "decline-enumeratum" % declineEnumV
)
)
.dependsOn(semantics, `backend-air`, `backend-ts`, linker)
@ -88,7 +88,6 @@ lazy val model = project
.settings(commons: _*)
.settings(
libraryDependencies ++= Seq(
airframeLog,
"org.typelevel" %% "cats-free" % catsV
)
)

View File

@ -3,4 +3,8 @@ package aqua.model
import aqua.types.ArrowType
import cats.data.NonEmptyMap
case class ServiceModel(name: String, arrows: NonEmptyMap[String, ArrowType]) extends Model
case class ServiceModel(
name: String,
arrows: NonEmptyMap[String, ArrowType],
defaultId: Option[ValueModel]
) extends Model

View File

@ -1,6 +1,6 @@
package aqua.model.func
import aqua.model.func.body.{CallArrowTag, FuncOp, OpTag}
import aqua.model.func.body.{AssignmentTag, CallArrowTag, FuncOp, OpTag}
import aqua.model.{ValueModel, VarModel}
import aqua.types.{ArrowType, Type}
import cats.Eval
@ -82,6 +82,15 @@ case class FuncCallable(
// Functions may export variables, so collect them
capturedValues
) {
case ((noNames, resolvedExports), tag @ AssignmentTag(value, assignTo)) =>
(noNames, resolvedExports + (assignTo -> value.resolveWith(resolvedExports))) -> Cofree[
Chain,
OpTag
](
tag.mapValues(_.resolveWith(resolvedExports)),
Eval.now(Chain.empty)
)
case ((noNames, resolvedExports), CallArrowTag(fn, c)) if allArrows.contains(fn) =>
// Apply arguments to a function recursion
val callResolved = c.mapValues(_.resolveWith(resolvedExports))

View File

@ -42,6 +42,7 @@ case class FuncOp(tree: Cofree[Chain, OpTag]) extends Model {
} match {
case c: CallArrowTag => c.copy(call = c.call.mapExport(n => vals.getOrElse(n, n)))
case c: CallServiceTag => c.copy(call = c.call.mapExport(n => vals.getOrElse(n, n)))
case a: AssignmentTag => a.copy(assignTo = vals.getOrElse(a.assignTo, a.assignTo))
case t: ForTag if vals.contains(t.item) => t.copy(item = vals(t.item))
case t: NextTag if vals.contains(t.item) => t.copy(item = vals(t.item))
case t => t

View File

@ -23,10 +23,17 @@ sealed trait OpTag {
call.mapValues(f),
pid.map(f)
)
case AssignmentTag(value, assignTo) =>
AssignmentTag(f(value), assignTo)
case AbilityIdTag(value, ability) =>
AssignmentTag(f(value), ability)
case _ => this
}
}
sealed trait NoAirTag extends OpTag
sealed trait GroupTag extends OpTag
sealed trait SeqGroupTag extends GroupTag
@ -55,6 +62,16 @@ case class CallArrowTag(
call: Call
) extends OpTag
case class AssignmentTag(
value: ValueModel,
assignTo: String
) extends NoAirTag
case class AbilityIdTag(
value: ValueModel,
service: String
) extends NoAirTag
case class CallServiceTag(
serviceId: ValueModel,
funcName: String,

View File

@ -10,6 +10,17 @@ import cats.free.Cofree
object Transform {
def defaultFilter(t: OpTag): Boolean = t match {
case _: NoAirTag => false
case _ => true
}
def clear(
tree: Cofree[Chain, OpTag],
filter: OpTag => Boolean = defaultFilter
): Cofree[Chain, OpTag] =
tree.copy(tail = tree.tail.map(_.filter(t => filter(t.head)).map(clear(_, filter))))
def forClient(func: FuncCallable, conf: BodyConfig): Cofree[Chain, OpTag] = {
val initCallable: InitPeerCallable = InitViaRelayCallable(
Chain.fromOption(conf.relayVarName).map(VarModel(_, ScalarType.string))
@ -38,13 +49,14 @@ object Transform {
callback,
conf.respFuncName
)
Topology.resolve(
errorsCatcher
.transform(
wrapFunc.resolve(func).value
)
.tree
clear(
Topology.resolve(
errorsCatcher
.transform(
wrapFunc.resolve(func).value
)
.tree
)
)
}
}

View File

@ -25,6 +25,7 @@ object ForExpr extends Expr.AndIndented {
Expr.defer(ForExpr) ::
CallArrowExpr ::
AbilityIdExpr ::
AssignmentExpr ::
Expr.defer(TryExpr) ::
Expr.defer(IfExpr) ::
Expr.defer(ElseOtherwiseExpr) ::

View File

@ -20,6 +20,7 @@ object FuncExpr extends Expr.AndIndented {
override def validChildren: List[Expr.Lexem] =
AbilityIdExpr ::
AssignmentExpr ::
ReturnExpr ::
ForExpr ::
Expr.defer(OnExpr) ::

View File

@ -16,6 +16,7 @@ object IfExpr extends Expr.AndIndented {
Expr.defer(OnExpr) ::
CallArrowExpr ::
AbilityIdExpr ::
AssignmentExpr ::
Expr.defer(TryExpr) ::
Expr.defer(ForExpr) ::
Expr.defer(IfExpr) ::

View File

@ -15,6 +15,7 @@ object OnExpr extends Expr.AndIndented {
Expr.defer(OnExpr) ::
CallArrowExpr ::
AbilityIdExpr ::
AssignmentExpr ::
ParExpr ::
Expr.defer(TryExpr) ::
Expr.defer(ForExpr) ::

View File

@ -19,6 +19,7 @@ object ExprSem {
): Prog[G, Model] =
expr match {
case expr: AbilityIdExpr[F] => new AbilityIdSem(expr).program[G]
case expr: AssignmentExpr[F] => new AssignmentSem(expr).program[G]
case expr: AliasExpr[F] => new AliasSem(expr).program[G]
case expr: ConstantExpr[F] => new ConstantSem(expr).program[G]
case expr: DeclareStreamExpr[F] => new DeclareStreamSem(expr).program[G]

View File

@ -1,15 +1,23 @@
package aqua.semantics.expr
import aqua.model.Model
import aqua.model.func.body.{AbilityIdTag, FuncOp}
import aqua.parser.expr.AbilityIdExpr
import aqua.semantics.Prog
import aqua.semantics.rules.ValuesAlgebra
import aqua.semantics.rules.abilities.AbilitiesAlgebra
import cats.syntax.flatMap._
import cats.syntax.functor._
class AbilityIdSem[F[_]](val expr: AbilityIdExpr[F]) extends AnyVal {
def program[Alg[_]](implicit A: AbilitiesAlgebra[F, Alg], V: ValuesAlgebra[F, Alg]): Prog[Alg, Model] =
V.ensureIsString(expr.id) >> A.setServiceId(expr.ability, expr.id) as Model.empty("Ability ID generates no model")
def program[Alg[_]](implicit
A: AbilitiesAlgebra[F, Alg],
V: ValuesAlgebra[F, Alg]
): Prog[Alg, Model] =
V.ensureIsString(expr.id) >> A.setServiceId(expr.ability, expr.id) >> V.valueToModel(
expr.id
) map {
case Some(id) => FuncOp.leaf(AbilityIdTag(id, expr.ability.value)): Model
case _ => Model.error("Cannot resolve ability ID")
}
}

View File

@ -0,0 +1,27 @@
package aqua.semantics.expr
import aqua.model.Model
import aqua.model.func.body.{AssignmentTag, FuncOp, FuncOps}
import aqua.parser.expr.{AbilityIdExpr, AssignmentExpr}
import aqua.semantics.Prog
import aqua.semantics.rules.ValuesAlgebra
import aqua.semantics.rules.abilities.AbilitiesAlgebra
import aqua.semantics.rules.names.NamesAlgebra
import cats.free.Free
import cats.syntax.flatMap._
import cats.syntax.functor._
class AssignmentSem[F[_]](val expr: AssignmentExpr[F]) extends AnyVal {
def program[Alg[_]](implicit
N: NamesAlgebra[F, Alg],
V: ValuesAlgebra[F, Alg]
): Prog[Alg, Model] =
V.valueToModel(expr.value).flatMap {
case Some(vm) =>
N.define(expr.variable, vm.lastType) as (FuncOp
.leaf(AssignmentTag(vm, expr.variable.value)): Model)
case _ => Free.pure[Alg, Model](Model.error("Cannot resolve assignment type"))
}
}

View File

@ -1,6 +1,6 @@
package aqua.semantics.expr
import aqua.model.{Model, ServiceModel}
import aqua.model.{Model, ServiceModel, ValueModel}
import aqua.parser.expr.ServiceExpr
import aqua.semantics.Prog
import aqua.semantics.rules.ValuesAlgebra
@ -10,7 +10,6 @@ import aqua.semantics.rules.types.TypesAlgebra
import cats.free.Free
import cats.syntax.apply._
import cats.syntax.flatMap._
import cats.syntax.functor._
class ServiceSem[F[_]](val expr: ServiceExpr[F]) extends AnyVal {
@ -26,18 +25,23 @@ class ServiceSem[F[_]](val expr: ServiceExpr[F]) extends AnyVal {
(A.purgeArrows(expr.name) <* A.endScope()).flatMap {
case Some(nel) =>
val arrows = nel.map(kv => kv._1.value -> kv._2).toNem
A.defineService(
expr.name,
arrows
).flatMap {
case true =>
val srv = ServiceModel(expr.name.value, arrows)
expr.id.fold(Free.pure[Alg, Model](srv))(idV =>
V.ensureIsString(idV) >> A.setServiceId(expr.name, idV) as (srv: Model)
for {
defaultId <- expr.id
.map(v => V.valueToModel(v))
.getOrElse(Free.pure[Alg, Option[ValueModel]](None))
defineResult <- A.defineService(
expr.name,
arrows,
defaultId
)
_ <- expr.id
.fold(Free.pure[Alg, Unit](()))(idV =>
(V.ensureIsString(idV) >> A.setServiceId(expr.name, idV)).map(_ => ())
)
case false =>
Free.pure(Model.empty("Service not created due to validation errors"))
}
} yield
if (defineResult) {
ServiceModel(expr.name.value, arrows, defaultId)
} else Model.empty("Service not created due to validation errors")
case None =>
Free.pure(Model.error("Service has no arrows, fails"))

View File

@ -1,5 +1,6 @@
package aqua.semantics.rules.abilities
import aqua.model.ValueModel
import aqua.parser.lexer.{Ability, Name, Token, Value}
import aqua.types.ArrowType
import cats.InjectK
@ -14,8 +15,12 @@ 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] =
Free.liftInject[Alg](DefineService[F](name, arrows))
def defineService(
name: Ability[F],
arrows: NonEmptyMap[String, ArrowType],
defaultId: Option[ValueModel]
): Free[Alg, Boolean] =
Free.liftInject[Alg](DefineService[F](name, arrows, defaultId))
def getArrow(name: Ability[F], arrow: Name[F]): Free[Alg, Option[ArrowType]] =
Free.liftInject[Alg](GetArrow[F](name, arrow))

View File

@ -1,12 +1,12 @@
package aqua.semantics.rules.abilities
import aqua.model.ServiceModel
import aqua.semantics.rules.{ReportError, StackInterpreter}
import aqua.parser.lexer.{Name, Value}
import aqua.semantics.rules.{ReportError, StackInterpreter}
import aqua.types.ArrowType
import cats.data.{NonEmptyList, State}
import cats.~>
import cats.syntax.functor._
import cats.~>
import monocle.Lens
import monocle.macros.GenLens
@ -105,8 +105,8 @@ class AbilitiesInterpreter[F[_], X](implicit
case None =>
modify(s =>
s.copy(
services =
s.services.updated(ds.name.value, ServiceModel(ds.name.value, ds.arrows)),
services = s.services
.updated(ds.name.value, ServiceModel(ds.name.value, ds.arrows, ds.defaultId)),
definitions = s.definitions.updated(ds.name.value, ds.name)
)
).as(true)

View File

@ -1,5 +1,6 @@
package aqua.semantics.rules.abilities
import aqua.model.ValueModel
import aqua.parser.lexer.{Ability, Name, Token, Value}
import aqua.types.ArrowType
import cats.data.{NonEmptyList, NonEmptyMap}
@ -11,8 +12,11 @@ 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, ArrowType],
defaultId: Option[ValueModel]
) extends AbilityOp[F, Boolean]
case class GetArrow[F[_]](name: Ability[F], arrow: Name[F]) extends AbilityOp[F, Option[ArrowType]]