mirror of
https://github.com/fluencelabs/aqua.git
synced 2025-04-24 22:42:13 +00:00
Streams support (#87)
* #29 going to support streams * Added StreamName * StreamName removed * Streams support works * Debug println removed Co-authored-by: Dima <dmitry.shakhtarin@fluence.ai>
This commit is contained in:
parent
3b3ff24133
commit
27f2912c5f
@ -12,6 +12,7 @@ data User:
|
||||
service Test("test"):
|
||||
getUserList: -> []User
|
||||
doSomething: -> bool
|
||||
check: bool -> ()
|
||||
|
||||
func initAfterJoin(me: User) -> []User:
|
||||
allUsers <- Test.getUserList()
|
||||
@ -26,3 +27,18 @@ func initAfterJoin(me: User) -> []User:
|
||||
Op.identity()
|
||||
par Test.doSomething()
|
||||
<- allUsers
|
||||
|
||||
func handleArr(arr: []bool):
|
||||
for b <- arr:
|
||||
Test.check(b)
|
||||
|
||||
|
||||
func checkStreams(ch: []string) -> []bool:
|
||||
stream: *bool
|
||||
stream <- Peer.is_connected("write once")
|
||||
stream <- Peer.is_connected("write twice")
|
||||
for b <- ch:
|
||||
on b:
|
||||
stream <- Peer.is_connected(b)
|
||||
handleArr(stream)
|
||||
<- stream
|
@ -3,6 +3,7 @@ package aqua.backend.air
|
||||
import aqua.model._
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.func.body._
|
||||
import aqua.types.StreamType
|
||||
import cats.Eval
|
||||
import cats.data.Chain
|
||||
import cats.free.Cofree
|
||||
@ -24,9 +25,13 @@ object AirGen {
|
||||
|
||||
def valueToData(vm: ValueModel): DataView = vm match {
|
||||
case LiteralModel(value) => DataView.StringScalar(value)
|
||||
case VarModel(name, lambda) =>
|
||||
if (lambda.isEmpty) DataView.Variable(name)
|
||||
else DataView.VarLens(name, lambdaToString(lambda.toList))
|
||||
case VarModel(name, t, lambda) =>
|
||||
val n = t match {
|
||||
case _: StreamType => "$" + name
|
||||
case _ => name
|
||||
}
|
||||
if (lambda.isEmpty) DataView.Variable(n)
|
||||
else DataView.VarLens(n, lambdaToString(lambda.toList))
|
||||
}
|
||||
|
||||
def opsToSingle(ops: Chain[AirGen]): AirGen = ops.toList match {
|
||||
@ -76,8 +81,11 @@ object AirGen {
|
||||
peerId.map(valueToData).getOrElse(DataView.InitPeerId),
|
||||
valueToData(serviceId),
|
||||
funcName,
|
||||
args.map(_.model).map(valueToData),
|
||||
exportTo
|
||||
args.map(valueToData),
|
||||
exportTo.map {
|
||||
case Call.Export(name, _: StreamType) => "$" + name
|
||||
case Call.Export(name, _) => name
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -39,7 +39,7 @@ case class TypescriptFunc(func: FuncCallable) {
|
||||
}.mkString("\n")
|
||||
|
||||
val retType = func.ret
|
||||
.map(_.`type`)
|
||||
.map(_._2)
|
||||
.fold("void")(typeToTs)
|
||||
|
||||
val returnVal =
|
||||
@ -90,6 +90,7 @@ object TypescriptFunc {
|
||||
|
||||
def typeToTs(t: Type): String = t match {
|
||||
case ArrayType(t) => typeToTs(t) + "[]"
|
||||
case StreamType(t) => typeToTs(t) + "[]"
|
||||
case pt: ProductType =>
|
||||
s"{${pt.fields.map(typeToTs).toNel.map(kv => kv._1 + ":" + kv._2).toList.mkString(";")}}"
|
||||
case st: ScalarType if ScalarType.number(st) => "number"
|
||||
|
@ -1,5 +1,6 @@
|
||||
package aqua.model
|
||||
|
||||
import aqua.types.Type
|
||||
import cats.data.Chain
|
||||
|
||||
sealed trait ValueModel {
|
||||
@ -16,7 +17,8 @@ sealed trait LambdaModel
|
||||
case object IntoArrayModel extends LambdaModel
|
||||
case class IntoFieldModel(field: String) extends LambdaModel
|
||||
|
||||
case class VarModel(name: String, lambda: Chain[LambdaModel] = Chain.empty) extends ValueModel {
|
||||
case class VarModel(name: String, `type`: Type, lambda: Chain[LambdaModel] = Chain.empty)
|
||||
extends ValueModel {
|
||||
def deriveFrom(vm: VarModel): VarModel = vm.copy(lambda = vm.lambda ++ lambda)
|
||||
|
||||
override def resolveWith(map: Map[String, ValueModel]): ValueModel = {
|
||||
@ -41,7 +43,7 @@ case class VarModel(name: String, lambda: Chain[LambdaModel] = Chain.empty) exte
|
||||
res <- two(variable)
|
||||
<- variable
|
||||
*/
|
||||
case vm @ VarModel(nn, _) if nn == name => deriveFrom(vm)
|
||||
case vm @ VarModel(nn, _, _) if nn == name => deriveFrom(vm)
|
||||
// it couldn't go to a cycle as long as the semantics protects it
|
||||
case _ => n.resolveWith(map)
|
||||
}
|
||||
|
@ -9,20 +9,19 @@ import cats.syntax.functor._
|
||||
* @param args Argument definitions
|
||||
* @param callWith Values provided for arguments
|
||||
*/
|
||||
case class ArgsCall(args: List[ArgDef], callWith: List[Call.Arg]) {
|
||||
case class ArgsCall(args: List[ArgDef], callWith: List[ValueModel]) {
|
||||
// Both arguments (arg names and types how they seen from the function body)
|
||||
// and values (value models and types how they seen on the call site)
|
||||
lazy val zipped: List[(ArgDef, Call.Arg)] = args zip callWith
|
||||
lazy val zipped: List[(ArgDef, ValueModel)] = args zip callWith
|
||||
|
||||
lazy val dataArgs: Map[String, ValueModel] =
|
||||
zipped.collect { case (ArgDef.Data(name, _), Call.Arg(value, _)) =>
|
||||
zipped.collect { case (ArgDef.Data(name, _), value) =>
|
||||
name -> value
|
||||
}.toMap
|
||||
|
||||
def arrowArgs(arrowsInScope: Map[String, FuncCallable]): Map[String, FuncCallable] =
|
||||
zipped.collect {
|
||||
case (ArgDef.Arrow(name, _), Call.Arg(VarModel(value, _), _))
|
||||
if arrowsInScope.contains(value) =>
|
||||
case (ArgDef.Arrow(name, _), VarModel(value, _, _)) if arrowsInScope.contains(value) =>
|
||||
name -> arrowsInScope(value)
|
||||
}.toMap
|
||||
}
|
||||
@ -33,7 +32,7 @@ object ArgsCall {
|
||||
arrow: ArrowType,
|
||||
argPrefix: String = "arg",
|
||||
retName: String = "init_call_res"
|
||||
): (ArgsDef, Call, Option[Call.Arg]) = {
|
||||
): (ArgsDef, Call, Option[Call.Export]) = {
|
||||
val argNamesTypes = arrow.args.zipWithIndex.map(iv => iv.map(i => argPrefix + i).swap)
|
||||
|
||||
val argsDef = ArgsDef(argNamesTypes.map {
|
||||
@ -43,12 +42,12 @@ object ArgsCall {
|
||||
|
||||
val call = Call(
|
||||
argNamesTypes.map { case (a, t) =>
|
||||
Call.Arg(VarModel(a), t)
|
||||
VarModel(a, t)
|
||||
},
|
||||
arrow.res.as(retName)
|
||||
arrow.res.map(Call.Export(retName, _))
|
||||
)
|
||||
|
||||
(argsDef, call, arrow.res.map(t => Call.Arg(VarModel(retName), t)))
|
||||
(argsDef, call, arrow.res.map(t => Call.Export(retName, t)))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -11,10 +11,10 @@ case class ArgsDef(args: List[ArgDef]) {
|
||||
|
||||
def types: List[Type] = args.map(_.`type`)
|
||||
|
||||
def toCallArgs: List[Call.Arg] = args.map(ad => Call.Arg(VarModel(ad.name), ad.`type`))
|
||||
def toCallArgs: List[VarModel] = args.map(ad => VarModel(ad.name, ad.`type`))
|
||||
|
||||
lazy val dataArgNames: Chain[String] = Chain.fromSeq(args.collect { case ArgDef.Data(n, _) =>
|
||||
n
|
||||
lazy val dataArgs: Chain[ArgDef.Data] = Chain.fromSeq(args.collect { case ad: ArgDef.Data =>
|
||||
ad
|
||||
})
|
||||
|
||||
lazy val arrowArgs: Chain[ArgDef.Arrow] = Chain.fromSeq(args.collect { case ad: ArgDef.Arrow =>
|
||||
|
@ -1,24 +1,24 @@
|
||||
package aqua.model.func
|
||||
|
||||
import aqua.model.ValueModel
|
||||
import aqua.model.{ValueModel, VarModel}
|
||||
import aqua.types.Type
|
||||
|
||||
case class Call(args: List[Call.Arg], exportTo: Option[String]) {
|
||||
case class Call(args: List[ValueModel], exportTo: Option[Call.Export]) {
|
||||
|
||||
def mapValues(f: ValueModel => ValueModel): Call =
|
||||
Call(
|
||||
args.map(_.mapValues(f)),
|
||||
args.map(f),
|
||||
exportTo
|
||||
)
|
||||
|
||||
def mapExport(f: String => String): Call = copy(exportTo = exportTo.map(f))
|
||||
def mapExport(f: String => String): Call = copy(exportTo = exportTo.map(_.mapName(f)))
|
||||
}
|
||||
|
||||
object Call {
|
||||
|
||||
case class Arg(model: ValueModel, `type`: Type) {
|
||||
case class Export(name: String, `type`: Type) {
|
||||
def mapName(f: String => String): Export = copy(f(name))
|
||||
|
||||
def mapValues(f: ValueModel => ValueModel): Arg =
|
||||
copy(f(model))
|
||||
def model: ValueModel = VarModel(name, `type`)
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ package aqua.model.func
|
||||
|
||||
import aqua.model.func.body.{CallArrowTag, FuncOp, OpTag}
|
||||
import aqua.model.{ValueModel, VarModel}
|
||||
import aqua.types.ArrowType
|
||||
import aqua.types.{ArrowType, DataType, Type}
|
||||
import cats.Eval
|
||||
import cats.data.Chain
|
||||
import cats.free.Cofree
|
||||
@ -11,7 +11,7 @@ case class FuncCallable(
|
||||
funcName: String,
|
||||
body: FuncOp,
|
||||
args: ArgsDef,
|
||||
ret: Option[Call.Arg],
|
||||
ret: Option[(ValueModel, Type)],
|
||||
capturedArrows: Map[String, FuncCallable],
|
||||
capturedValues: Map[String, ValueModel]
|
||||
) {
|
||||
@ -19,7 +19,7 @@ case class FuncCallable(
|
||||
def arrowType: ArrowType =
|
||||
ArrowType(
|
||||
args.types,
|
||||
ret.map(_.`type`)
|
||||
ret.map(_._2)
|
||||
)
|
||||
|
||||
def findNewNames(forbidden: Set[String], introduce: Set[String]): Map[String, String] =
|
||||
@ -52,7 +52,7 @@ case class FuncCallable(
|
||||
val treeWithValues = body.resolveValues(argsToData)
|
||||
|
||||
// Function body on its own defines some values; collect their names
|
||||
val treeDefines = treeWithValues.definesValueNames.value -- call.exportTo
|
||||
val treeDefines = treeWithValues.definesValueNames.value -- call.exportTo.map(_.name)
|
||||
|
||||
// We have some names in scope (forbiddenNames), can't introduce them again; so find new names
|
||||
val shouldRename = findNewNames(forbiddenNames, treeDefines)
|
||||
@ -61,7 +61,7 @@ case class FuncCallable(
|
||||
if (shouldRename.isEmpty) treeWithValues else treeWithValues.rename(shouldRename)
|
||||
|
||||
// Result could be derived from arguments, or renamed; take care about that
|
||||
val result = ret.map(_.model).map(_.resolveWith(argsToData)).map {
|
||||
val result = ret.map(_._1).map(_.resolveWith(argsToData)).map {
|
||||
case v: VarModel if shouldRename.contains(v.name) => v.copy(shouldRename(v.name))
|
||||
case v => v
|
||||
}
|
||||
@ -79,8 +79,8 @@ case class FuncCallable(
|
||||
case ((noNames, resolvedExports), CallArrowTag(fn, c)) if allArrows.contains(fn) =>
|
||||
// Apply arguments to a function – recursion
|
||||
val callResolved = c.mapValues(_.resolveWith(resolvedExports))
|
||||
val possibleArrowNames = callResolved.args.collect {
|
||||
case Call.Arg(VarModel(m, _), _: ArrowType) => m
|
||||
val possibleArrowNames = callResolved.args.collect { case VarModel(m, _: ArrowType, _) =>
|
||||
m
|
||||
}.toSet
|
||||
|
||||
val (appliedOp, value) =
|
||||
@ -92,7 +92,10 @@ case class FuncCallable(
|
||||
// TODO: actually it's done and dropped – so keep and pass it instead
|
||||
val newNames = appliedOp.definesValueNames.value
|
||||
// At the very end, will need to resolve what is used as results with the result values
|
||||
(noNames ++ newNames, resolvedExports ++ c.exportTo.zip(value)) -> appliedOp.tree
|
||||
(
|
||||
noNames ++ newNames,
|
||||
resolvedExports ++ c.exportTo.map(_.name).zip(value)
|
||||
) -> appliedOp.tree
|
||||
case (acc @ (_, resolvedExports), tag) =>
|
||||
tag match {
|
||||
case CallArrowTag(fn, _) if !allArrows.contains(fn) =>
|
||||
|
@ -2,11 +2,12 @@ package aqua.model.func
|
||||
|
||||
import aqua.model.func.body.FuncOp
|
||||
import aqua.model.{Model, ValueModel}
|
||||
import aqua.types.Type
|
||||
|
||||
case class FuncModel(
|
||||
name: String,
|
||||
args: ArgsDef,
|
||||
ret: Option[Call.Arg],
|
||||
ret: Option[(ValueModel, Type)],
|
||||
body: FuncOp
|
||||
) extends Model {
|
||||
|
||||
|
@ -23,9 +23,9 @@ case class FuncOp(tree: Cofree[Chain, OpTag]) extends Model {
|
||||
|
||||
def definesValueNames: Eval[Set[String]] = cata[Set[String]] {
|
||||
case (CallArrowTag(_, Call(_, Some(export))), acc) =>
|
||||
Eval.later(acc.foldLeft(Set(export))(_ ++ _))
|
||||
Eval.later(acc.foldLeft(Set(export.name))(_ ++ _))
|
||||
case (CallServiceTag(_, _, Call(_, Some(export)), _), acc) =>
|
||||
Eval.later(acc.foldLeft(Set(export))(_ ++ _))
|
||||
Eval.later(acc.foldLeft(Set(export.name))(_ ++ _))
|
||||
case (NextTag(export), acc) => Eval.later(acc.foldLeft(Set(export))(_ ++ _))
|
||||
case (_, acc) => Eval.later(acc.foldLeft(Set.empty[String])(_ ++ _))
|
||||
}
|
||||
|
@ -3,23 +3,25 @@ package aqua.model.transform
|
||||
import aqua.model.ValueModel
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.func.body.{FuncOp, FuncOps}
|
||||
import aqua.types.DataType
|
||||
|
||||
trait ArgsProvider {
|
||||
def transform(op: FuncOp): FuncOp
|
||||
}
|
||||
|
||||
case class ArgsFromService(dataServiceId: ValueModel, names: Seq[String]) extends ArgsProvider {
|
||||
case class ArgsFromService(dataServiceId: ValueModel, names: List[(String, DataType)])
|
||||
extends ArgsProvider {
|
||||
|
||||
def getDataOp(name: String): FuncOp =
|
||||
def getDataOp(name: String, t: DataType): FuncOp =
|
||||
FuncOps.callService(
|
||||
dataServiceId,
|
||||
name,
|
||||
Call(Nil, Some(name))
|
||||
Call(Nil, Some(Call.Export(name, t)))
|
||||
)
|
||||
|
||||
def transform(op: FuncOp): FuncOp =
|
||||
FuncOps.seq(
|
||||
names.map(getDataOp) :+ op: _*
|
||||
names.map((getDataOp _).tupled) :+ op: _*
|
||||
)
|
||||
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package aqua.model.transform
|
||||
import aqua.model.{LiteralModel, ValueModel}
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.func.body.{FuncOp, FuncOps}
|
||||
import aqua.types.ScalarType.string
|
||||
|
||||
case class ErrorsCatcher(
|
||||
enabled: Boolean,
|
||||
@ -28,7 +27,7 @@ case class ErrorsCatcher(
|
||||
|
||||
object ErrorsCatcher {
|
||||
// TODO not a string
|
||||
val lastErrorArg: Call.Arg = Call.Arg(LiteralModel("%last_error%"), string)
|
||||
val lastErrorArg: ValueModel = LiteralModel("%last_error%")
|
||||
|
||||
val lastErrorCall: Call = Call(
|
||||
lastErrorArg :: Nil,
|
||||
|
@ -5,6 +5,7 @@ import aqua.model.func.{ArgDef, ArgsCall, ArgsDef, Call, FuncCallable}
|
||||
import aqua.model.func.body.{FuncOp, FuncOps}
|
||||
import aqua.types.ArrowType
|
||||
import cats.Eval
|
||||
import cats.syntax.apply._
|
||||
|
||||
case class ResolveFunc(
|
||||
transform: FuncOp => FuncOp,
|
||||
@ -14,11 +15,11 @@ case class ResolveFunc(
|
||||
arrowCallbackPrefix: String = "init_peer_callable_"
|
||||
) {
|
||||
|
||||
def returnCallback(func: FuncCallable): Option[FuncOp] = func.ret.map { retArg =>
|
||||
def returnCallback(func: FuncCallable): Option[FuncOp] = func.ret.map { case (retModel, _) =>
|
||||
callback(
|
||||
respFuncName,
|
||||
Call(
|
||||
retArg :: Nil,
|
||||
retModel :: Nil,
|
||||
None
|
||||
)
|
||||
)
|
||||
@ -30,7 +31,7 @@ case class ResolveFunc(
|
||||
arrowCallbackPrefix + name,
|
||||
callback(name, call),
|
||||
args,
|
||||
ret,
|
||||
(ret.map(_.model), arrowType.res).mapN(_ -> _),
|
||||
Map.empty,
|
||||
Map.empty
|
||||
)
|
||||
@ -63,7 +64,7 @@ case class ResolveFunc(
|
||||
def resolve(func: FuncCallable, funcArgName: String = "_func"): Eval[FuncOp] =
|
||||
wrap(func)
|
||||
.resolve(
|
||||
Call(Call.Arg(VarModel(funcArgName), func.arrowType) :: Nil, None),
|
||||
Call(VarModel(funcArgName, func.arrowType) :: Nil, None),
|
||||
Map(funcArgName -> func),
|
||||
Set.empty
|
||||
)
|
||||
|
@ -3,6 +3,7 @@ package aqua.model.transform
|
||||
import aqua.model.func.body._
|
||||
import aqua.model.func.FuncCallable
|
||||
import aqua.model.VarModel
|
||||
import aqua.types.ScalarType
|
||||
import cats.data.Chain
|
||||
import cats.free.Cofree
|
||||
|
||||
@ -10,7 +11,7 @@ object Transform {
|
||||
|
||||
def forClient(func: FuncCallable, conf: BodyConfig): Cofree[Chain, OpTag] = {
|
||||
val initCallable: InitPeerCallable = InitViaRelayCallable(
|
||||
Chain.one(VarModel(conf.relayVarName))
|
||||
Chain.one(VarModel(conf.relayVarName, ScalarType.string))
|
||||
)
|
||||
val errorsCatcher = ErrorsCatcher(
|
||||
enabled = conf.wrapWithXor,
|
||||
@ -19,7 +20,12 @@ object Transform {
|
||||
initCallable
|
||||
)
|
||||
val argsProvider: ArgsProvider =
|
||||
ArgsFromService(conf.dataSrvId, conf.relayVarName +: func.args.dataArgNames.toList)
|
||||
ArgsFromService(
|
||||
conf.dataSrvId,
|
||||
conf.relayVarName -> ScalarType.string :: func.args.dataArgs.toList.map(add =>
|
||||
add.name -> add.dataType
|
||||
)
|
||||
)
|
||||
|
||||
val transform =
|
||||
errorsCatcher.transform _ compose initCallable.transform compose argsProvider.transform
|
||||
|
@ -1,16 +1,7 @@
|
||||
package aqua.model
|
||||
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.func.body.{
|
||||
CallServiceTag,
|
||||
FuncOp,
|
||||
FuncOps,
|
||||
MatchMismatchTag,
|
||||
OnTag,
|
||||
OpTag,
|
||||
SeqTag,
|
||||
XorTag
|
||||
}
|
||||
import aqua.model.func.body._
|
||||
import aqua.model.transform.BodyConfig
|
||||
import aqua.types.ScalarType
|
||||
import cats.Eval
|
||||
@ -30,10 +21,9 @@ case class Node(tag: OpTag, ops: List[Node] = Nil) {
|
||||
else
|
||||
Console.BLUE + left + Console.RED + " != " + Console.YELLOW + right)
|
||||
|
||||
private def diffArg(left: Call.Arg, right: Call.Arg): String =
|
||||
private def diffArg(left: ValueModel, right: ValueModel): String =
|
||||
Console.GREEN + "(" +
|
||||
equalOrNot(left.model, right.model) + Console.GREEN + ", " +
|
||||
equalOrNot(left.`type`, right.`type`) + Console.GREEN + ")"
|
||||
equalOrNot(left, right) + Console.GREEN + ")"
|
||||
|
||||
private def diffCall(left: Call, right: Call): String =
|
||||
if (left == right) Console.GREEN + left + Console.RESET
|
||||
@ -95,7 +85,7 @@ object Node {
|
||||
Cofree(tree.tag, Eval.later(Chain.fromSeq(tree.ops.map(nodeToCof))))
|
||||
|
||||
val relay = LiteralModel("relay")
|
||||
val relayV = VarModel("relay")
|
||||
val relayV = VarModel("relay", ScalarType.string)
|
||||
val initPeer = LiteralModel.initPeerId
|
||||
val emptyCall = Call(Nil, None)
|
||||
val otherPeer = LiteralModel("other-peer")
|
||||
@ -111,7 +101,7 @@ object Node {
|
||||
CallServiceTag(
|
||||
bc.errorHandlingCallback,
|
||||
bc.errorFuncName,
|
||||
Call(Call.Arg(LiteralModel("%last_error%"), ScalarType.string) :: Nil, None),
|
||||
Call(LiteralModel("%last_error%") :: Nil, None),
|
||||
Option(on)
|
||||
)
|
||||
)
|
||||
@ -120,7 +110,7 @@ object Node {
|
||||
CallServiceTag(
|
||||
bc.callbackSrvId,
|
||||
bc.respFuncName,
|
||||
Call(Call.Arg(value, ScalarType.string) :: Nil, None),
|
||||
Call(value :: Nil, None),
|
||||
Option(on)
|
||||
)
|
||||
)
|
||||
@ -129,7 +119,7 @@ object Node {
|
||||
CallServiceTag(
|
||||
bc.dataSrvId,
|
||||
name,
|
||||
Call(Nil, Some(name)),
|
||||
Call(Nil, Some(Call.Export(name, ScalarType.string))),
|
||||
Option(on)
|
||||
)
|
||||
)
|
||||
|
@ -19,7 +19,7 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
||||
"ret",
|
||||
FuncOp(on(otherPeer, Nil, call(1))),
|
||||
ArgsDef.empty,
|
||||
Some(Call.Arg(ret, ScalarType.string)),
|
||||
Some((ret, ScalarType.string)),
|
||||
Map.empty,
|
||||
Map.empty
|
||||
)
|
||||
@ -57,7 +57,7 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
||||
"ret",
|
||||
FuncOp(seq(call(0), on(otherPeer, Nil, call(1)))),
|
||||
ArgsDef.empty,
|
||||
Some(Call.Arg(ret, ScalarType.string)),
|
||||
Some((ret, ScalarType.string)),
|
||||
Map.empty,
|
||||
Map.empty
|
||||
)
|
||||
@ -104,9 +104,18 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
||||
val f1: FuncCallable =
|
||||
FuncCallable(
|
||||
"f1",
|
||||
FuncOp(Node(CallServiceTag(LiteralModel("\"srv1\""), "foo", Call(Nil, Some("v")), None))),
|
||||
FuncOp(
|
||||
Node(
|
||||
CallServiceTag(
|
||||
LiteralModel("\"srv1\""),
|
||||
"foo",
|
||||
Call(Nil, Some(Call.Export("v", ScalarType.string))),
|
||||
None
|
||||
)
|
||||
)
|
||||
),
|
||||
ArgsDef.empty,
|
||||
Some(Call.Arg(VarModel("v"), ScalarType.string)),
|
||||
Some((VarModel("v", ScalarType.string), ScalarType.string)),
|
||||
Map.empty,
|
||||
Map.empty
|
||||
)
|
||||
@ -115,10 +124,10 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
||||
FuncCallable(
|
||||
"f2",
|
||||
FuncOp(
|
||||
Node(CallArrowTag("callable", Call(Nil, Some("v"))))
|
||||
Node(CallArrowTag("callable", Call(Nil, Some(Call.Export("v", ScalarType.string)))))
|
||||
),
|
||||
ArgsDef.empty,
|
||||
Some(Call.Arg(VarModel("v"), ScalarType.string)),
|
||||
Some((VarModel("v", ScalarType.string), ScalarType.string)),
|
||||
Map("callable" -> f1),
|
||||
Map.empty
|
||||
)
|
||||
@ -134,12 +143,17 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
||||
seq(
|
||||
dataCall(bc, "relay", initPeer),
|
||||
Node(
|
||||
CallServiceTag(LiteralModel("\"srv1\""), "foo", Call(Nil, Some("v")), Some(initPeer))
|
||||
CallServiceTag(
|
||||
LiteralModel("\"srv1\""),
|
||||
"foo",
|
||||
Call(Nil, Some(Call.Export("v", ScalarType.string))),
|
||||
Some(initPeer)
|
||||
)
|
||||
),
|
||||
on(
|
||||
initPeer,
|
||||
relayV :: Nil,
|
||||
respCall(bc, VarModel("v"), initPeer)
|
||||
respCall(bc, VarModel("v", ScalarType.string), initPeer)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -12,7 +12,8 @@ case class ArrowTypeExpr[F[_]](name: Name[F], `type`: ArrowTypeToken[F]) extends
|
||||
object ArrowTypeExpr extends Expr.Leaf {
|
||||
|
||||
override def p[F[_]: LiftParser: Comonad]: Parser[ArrowTypeExpr[F]] =
|
||||
(Name.p[F] ~ ((` : ` *> ArrowTypeToken.`arrowdef`[F]) | ArrowTypeToken.`arrowWithNames`)).map {
|
||||
(Name
|
||||
.p[F] ~ ((` : ` *> ArrowTypeToken.`arrowdef`[F]) | ArrowTypeToken.`arrowWithNames`)).map {
|
||||
case (name, t) =>
|
||||
ArrowTypeExpr(name, t)
|
||||
}
|
||||
|
@ -0,0 +1,19 @@
|
||||
package aqua.parser.expr
|
||||
|
||||
import aqua.parser.Expr
|
||||
import aqua.parser.lexer.{Name, Token, TypeToken}
|
||||
import aqua.parser.lift.LiftParser
|
||||
import cats.Comonad
|
||||
import cats.parse.Parser
|
||||
import Token._
|
||||
|
||||
case class DeclareStreamExpr[F[_]](name: Name[F], `type`: TypeToken[F]) extends Expr[F]
|
||||
|
||||
object DeclareStreamExpr extends Expr.Leaf {
|
||||
|
||||
override def p[F[_]: LiftParser: Comonad]: Parser[DeclareStreamExpr[F]] =
|
||||
((Name.p[F] <* ` : `) ~ TypeToken.`typedef`[F]).map { case (name, t) =>
|
||||
DeclareStreamExpr(name, t)
|
||||
}
|
||||
|
||||
}
|
@ -12,7 +12,7 @@ case class FieldTypeExpr[F[_]](name: Name[F], `type`: DataTypeToken[F]) extends
|
||||
object FieldTypeExpr extends Expr.Leaf {
|
||||
|
||||
override def p[F[_]: LiftParser: Comonad]: Parser[FieldTypeExpr[F]] =
|
||||
((Name.p[F] <* ` : `) ~ DataTypeToken.`datatypedef`[F]).map {
|
||||
case (name, t) => FieldTypeExpr(name, t)
|
||||
((Name.p[F] <* ` : `) ~ DataTypeToken.`datatypedef`[F]).map { case (name, t) =>
|
||||
FieldTypeExpr(name, t)
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,8 @@ object FuncExpr
|
||||
ParExpr,
|
||||
ForExpr,
|
||||
IfExpr,
|
||||
ElseOtherwiseExpr
|
||||
ElseOtherwiseExpr,
|
||||
DeclareStreamExpr
|
||||
) {
|
||||
|
||||
override def p[F[_]: LiftParser: Comonad]: Parser[FuncExpr[F]] =
|
||||
|
@ -10,7 +10,7 @@ case class Arg[F[_]](name: Name[F], `type`: TypeToken[F])
|
||||
object Arg {
|
||||
|
||||
def p[F[_]: LiftParser: Comonad]: P[Arg[F]] =
|
||||
((Name.p[F] <* ` : `) ~ TypeToken.`typedef`[F]).map {
|
||||
case (name, t) => Arg(name, t)
|
||||
((Name.p[F] <* ` : `) ~ TypeToken.`typedef`[F]).map { case (name, t) =>
|
||||
Arg(name, t)
|
||||
}
|
||||
}
|
||||
|
@ -43,9 +43,7 @@ object Token {
|
||||
val `:` : P[Unit] = P.char(':')
|
||||
val ` : ` : P[Unit] = P.char(':').surroundedBy(` `.?)
|
||||
|
||||
val `name`: P[String] = (P.charIn(az) ~ P.charsWhile(anum_).?).map { case (c, s) ⇒
|
||||
c.toString ++ s.getOrElse("")
|
||||
}
|
||||
val `name`: P[String] = (P.charIn(az) ~ P.charsWhile(anum_).?).string
|
||||
|
||||
val `Class`: P[String] = (P.charIn(AZ) ~ P.charsWhile(anum_).?).map { case (c, s) ⇒
|
||||
c.toString ++ s.getOrElse("")
|
||||
@ -59,8 +57,10 @@ object Token {
|
||||
val `.` : P[Unit] = P.char('.')
|
||||
val `"` : P[Unit] = P.char('"')
|
||||
val `*` : P[Unit] = P.char('*')
|
||||
val `[]` : P[Unit] = P.string("[]")
|
||||
val `(` : P[Unit] = ` `.?.with1 *> P.char('(') <* ` `.?
|
||||
val `)` : P[Unit] = ` `.?.with1 *> P.char(')') <* ` `.?
|
||||
val `()` : P[Unit] = P.string("()")
|
||||
val ` -> ` : P[Unit] = ` `.?.with1 *> P.string("->") <* ` `.?
|
||||
val ` <- ` : P[Unit] = (` `.?.with1 *> P.string("<-") <* ` `.?).backtrack
|
||||
val `=` : P[Unit] = P.string("=")
|
||||
|
@ -17,6 +17,18 @@ case class ArrayTypeToken[F[_]: Comonad](override val unit: F[Unit], data: DataT
|
||||
override def as[T](v: T): F[T] = unit.as(v)
|
||||
}
|
||||
|
||||
case class StreamTypeToken[F[_]: Comonad](override val unit: F[Unit], data: DataTypeToken[F])
|
||||
extends DataTypeToken[F] {
|
||||
override def as[T](v: T): F[T] = unit.as(v)
|
||||
}
|
||||
|
||||
object StreamTypeToken {
|
||||
|
||||
def `streamtypedef`[F[_]: LiftParser: Comonad]: P[StreamTypeToken[F]] =
|
||||
(`*`.lift ~ DataTypeToken.`datatypedef`[F]).map(ud => StreamTypeToken(ud._1, ud._2))
|
||||
|
||||
}
|
||||
|
||||
case class CustomTypeToken[F[_]: Comonad](name: F[String]) extends DataTypeToken[F] {
|
||||
override def as[T](v: T): F[T] = name.as(v)
|
||||
|
||||
@ -64,7 +76,7 @@ object ArrowTypeToken {
|
||||
def `arrowdef`[F[_]: LiftParser: Comonad]: P[ArrowTypeToken[F]] =
|
||||
(comma0(DataTypeToken.`datatypedef`).with1 ~ ` -> `.lift ~
|
||||
(DataTypeToken.`datatypedef`
|
||||
.map(Some(_)) | P.string("()").as(None))).map { case ((args, point), res) ⇒
|
||||
.map(Some(_)) | `()`.as(None))).map { case ((args, point), res) ⇒
|
||||
ArrowTypeToken(point, args, res)
|
||||
}
|
||||
|
||||
@ -75,27 +87,23 @@ object ArrowTypeToken {
|
||||
}
|
||||
}
|
||||
|
||||
case class AquaArrowType[F[_]](args: List[TypeToken[F]], res: Option[DataTypeToken[F]])
|
||||
extends ArrowDef[F] {
|
||||
override def argTypes: List[TypeToken[F]] = args
|
||||
|
||||
override def resType: Option[DataTypeToken[F]] = res
|
||||
}
|
||||
|
||||
object DataTypeToken {
|
||||
|
||||
def `arraytypedef`[F[_]: LiftParser: Comonad]: P[ArrayTypeToken[F]] =
|
||||
(P.string("[]").lift ~ `datatypedef`[F]).map(ud => ArrayTypeToken(ud._1, ud._2))
|
||||
(`[]`.lift ~ `datatypedef`[F]).map(ud => ArrayTypeToken(ud._1, ud._2))
|
||||
|
||||
def `datatypedef`[F[_]: LiftParser: Comonad]: P[DataTypeToken[F]] =
|
||||
P.oneOf(
|
||||
P.defer(`arraytypedef`[F]) :: BasicTypeToken.`basictypedef`[F] :: CustomTypeToken.ct[F] :: Nil
|
||||
P.defer(`arraytypedef`[F]) :: P.defer(StreamTypeToken.`streamtypedef`) :: BasicTypeToken
|
||||
.`basictypedef`[F] :: CustomTypeToken.ct[F] :: Nil
|
||||
)
|
||||
}
|
||||
|
||||
object TypeToken {
|
||||
|
||||
def `typedef`[F[_]: LiftParser: Comonad]: P[TypeToken[F]] =
|
||||
P.oneOf(ArrowTypeToken.`arrowdef`.backtrack :: DataTypeToken.`datatypedef` :: Nil)
|
||||
P.oneOf(
|
||||
ArrowTypeToken.`arrowdef`.backtrack :: DataTypeToken.`datatypedef` :: Nil
|
||||
)
|
||||
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ object ExprSem {
|
||||
case expr: AbilityIdExpr[F] => new AbilityIdSem(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]
|
||||
case expr: ArrowTypeExpr[F] => new ArrowTypeSem(expr).program[G]
|
||||
case expr: CallArrowExpr[F] => new CallArrowSem(expr).program[G]
|
||||
case expr: DataStructExpr[F] => new DataStructSem(expr).program[G]
|
||||
|
@ -1,6 +1,6 @@
|
||||
package aqua.semantics.expr
|
||||
|
||||
import aqua.model.Model
|
||||
import aqua.model.{Model, ValueModel}
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.func.body.{CallArrowTag, CallServiceTag, FuncOp}
|
||||
import aqua.parser.expr.CallArrowExpr
|
||||
@ -9,7 +9,7 @@ import aqua.semantics.rules.ValuesAlgebra
|
||||
import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
||||
import aqua.semantics.rules.names.NamesAlgebra
|
||||
import aqua.semantics.rules.types.TypesAlgebra
|
||||
import aqua.types.ArrowType
|
||||
import aqua.types.{ArrowType, ScalarType, StreamType, Type}
|
||||
import cats.free.Free
|
||||
import cats.syntax.apply._
|
||||
import cats.syntax.flatMap._
|
||||
@ -25,16 +25,29 @@ class CallArrowSem[F[_]](val expr: CallArrowExpr[F]) extends AnyVal {
|
||||
at: ArrowType
|
||||
)(implicit
|
||||
N: NamesAlgebra[F, Alg],
|
||||
T: TypesAlgebra[F, Alg],
|
||||
V: ValuesAlgebra[F, Alg]
|
||||
): Free[Alg, List[Call.Arg]] =
|
||||
): Free[Alg, (List[ValueModel], Option[Type])] =
|
||||
V.checkArguments(expr.funcName, at, args) >> variable
|
||||
.fold(freeUnit[Alg])(exportVar =>
|
||||
.fold(freeUnit[Alg].as(Option.empty[Type]))(exportVar =>
|
||||
at.res.fold(
|
||||
// TODO: error! we're trying to export variable, but function has no export type
|
||||
freeUnit[Alg]
|
||||
)(resType => N.define(exportVar, resType).void)
|
||||
) >> args.foldLeft(Free.pure[Alg, List[Call.Arg]](Nil)) { case (acc, v) =>
|
||||
(acc, V.resolveType(v)).mapN((a, b) => a ++ b.map(Call.Arg(ValuesAlgebra.valueToModel(v), _)))
|
||||
freeUnit[Alg].as(Option.empty[Type])
|
||||
)(resType =>
|
||||
N.read(exportVar, mustBeDefined = false).flatMap {
|
||||
case Some(t @ StreamType(st)) =>
|
||||
T.ensureTypeMatches(exportVar, st, resType).as(Option(t))
|
||||
case _ => N.define(exportVar, resType).as(at.res)
|
||||
}
|
||||
)
|
||||
) >>= { (v: Option[Type]) =>
|
||||
args
|
||||
.foldLeft(Free.pure[Alg, List[ValueModel]](Nil)) { case (acc, v) =>
|
||||
(acc, V.resolveType(v)).mapN((a, b) =>
|
||||
a ++ b.map(bt => ValuesAlgebra.valueToModel(v, bt))
|
||||
)
|
||||
}
|
||||
.map(_ -> v)
|
||||
}
|
||||
|
||||
private def toModel[Alg[_]](implicit
|
||||
@ -50,30 +63,29 @@ class CallArrowSem[F[_]](val expr: CallArrowExpr[F]) extends AnyVal {
|
||||
Option(at -> sid) // Here we assume that Ability is a Service that must be resolved
|
||||
case _ => None
|
||||
}.flatMap(_.fold(Free.pure[Alg, Option[FuncOp]](None)) { case (arrowType, serviceId) =>
|
||||
checkArgsRes(arrowType)
|
||||
.map(argsResolved =>
|
||||
FuncOp.leaf(
|
||||
CallServiceTag(
|
||||
serviceId = ValuesAlgebra.valueToModel(serviceId),
|
||||
funcName = funcName.value,
|
||||
Call(argsResolved, variable.map(_.value))
|
||||
)
|
||||
checkArgsRes(arrowType).map { case (argsResolved, t) =>
|
||||
FuncOp.leaf(
|
||||
CallServiceTag(
|
||||
// TODO service id type should not be hardcoded
|
||||
serviceId = ValuesAlgebra.valueToModel(serviceId, ScalarType.string),
|
||||
funcName = funcName.value,
|
||||
Call(argsResolved, (variable.map(_.value), t).mapN(Call.Export))
|
||||
)
|
||||
)
|
||||
}
|
||||
.map(Option(_))
|
||||
})
|
||||
case None =>
|
||||
N.readArrow(funcName)
|
||||
.flatMap(_.fold(Free.pure[Alg, Option[FuncOp]](None)) { arrowType =>
|
||||
checkArgsRes(arrowType)
|
||||
.map(argsResolved =>
|
||||
FuncOp.leaf(
|
||||
CallArrowTag(
|
||||
funcName = funcName.value,
|
||||
Call(argsResolved, variable.map(_.value))
|
||||
)
|
||||
checkArgsRes(arrowType).map { case (argsResolved, t) =>
|
||||
FuncOp.leaf(
|
||||
CallArrowTag(
|
||||
funcName = funcName.value,
|
||||
Call(argsResolved, (variable.map(_.value), t).mapN(Call.Export))
|
||||
)
|
||||
)
|
||||
}
|
||||
.map(Option(_))
|
||||
})
|
||||
}
|
||||
@ -84,6 +96,6 @@ class CallArrowSem[F[_]](val expr: CallArrowExpr[F]) extends AnyVal {
|
||||
T: TypesAlgebra[F, Alg],
|
||||
V: ValuesAlgebra[F, Alg]
|
||||
): Prog[Alg, Model] =
|
||||
toModel[Alg].map(_.getOrElse(Model.error("Coalgebra can't be converted to Model")))
|
||||
toModel[Alg].map(_.getOrElse(Model.error("CallArrow can't be converted to Model")))
|
||||
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ class ConstantSem[F[_]](val expr: ConstantExpr[F]) extends AnyVal {
|
||||
case (_, Some(t), _) =>
|
||||
N.defineConstant(expr.name, t) as (ConstantModel(
|
||||
expr.name.value,
|
||||
ValuesAlgebra.valueToModel(expr.value)
|
||||
ValuesAlgebra.valueToModel(expr.value, t)
|
||||
): Model)
|
||||
}
|
||||
} yield model
|
||||
|
@ -0,0 +1,35 @@
|
||||
package aqua.semantics.expr
|
||||
|
||||
import aqua.model.Model
|
||||
import aqua.parser.expr.DeclareStreamExpr
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.names.NamesAlgebra
|
||||
import aqua.semantics.rules.types.TypesAlgebra
|
||||
import aqua.types.{ArrayType, StreamType}
|
||||
import cats.free.Free
|
||||
|
||||
class DeclareStreamSem[F[_]](val expr: DeclareStreamExpr[F]) {
|
||||
|
||||
def program[Alg[_]](implicit
|
||||
N: NamesAlgebra[F, Alg],
|
||||
T: TypesAlgebra[F, Alg]
|
||||
): Prog[Alg, Model] =
|
||||
Prog.leaf(
|
||||
T.resolveType(expr.`type`)
|
||||
.flatMap {
|
||||
case Some(t: StreamType) =>
|
||||
N.define(expr.name, t)
|
||||
case Some(at @ ArrayType(t)) =>
|
||||
T.ensureTypeMatches(expr.`type`, StreamType(t), at)
|
||||
case Some(t) =>
|
||||
T.ensureTypeMatches(expr.`type`, StreamType(t), t)
|
||||
case None =>
|
||||
Free.pure[Alg, Boolean](false)
|
||||
}
|
||||
.map {
|
||||
case true => Model.empty(s"Name `${expr.name.value}` defined successfully")
|
||||
case false => Model.error(s"Name `${expr.name.value}` not defined")
|
||||
}
|
||||
)
|
||||
|
||||
}
|
@ -7,7 +7,7 @@ import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.ValuesAlgebra
|
||||
import aqua.semantics.rules.names.NamesAlgebra
|
||||
import aqua.semantics.rules.types.TypesAlgebra
|
||||
import aqua.types.{ArrayType, DataType}
|
||||
import aqua.types.{ArrayType, StreamType, Type}
|
||||
import cats.data.Chain
|
||||
import cats.free.Free
|
||||
import cats.syntax.flatMap._
|
||||
@ -21,18 +21,20 @@ class ForSem[F[_]](val expr: ForExpr[F]) extends AnyVal {
|
||||
T: TypesAlgebra[F, Alg]
|
||||
): Prog[Alg, Model] =
|
||||
Prog.around(
|
||||
N.beginScope(expr.item) >> V.resolveType(expr.iterable).flatMap {
|
||||
case Some(ArrayType(t)) =>
|
||||
N.define(expr.item, t).void
|
||||
case Some(dt: DataType) => T.ensureTypeMatches(expr.iterable, ArrayType(dt), dt).void
|
||||
case _ => Free.pure[Alg, Unit](())
|
||||
N.beginScope(expr.item) >> V.resolveType(expr.iterable).flatMap[Option[Type]] {
|
||||
case Some(at @ ArrayType(t)) =>
|
||||
N.define(expr.item, t).as(Option(at))
|
||||
case Some(st @ StreamType(t)) =>
|
||||
N.define(expr.item, t).as(Option(st))
|
||||
case Some(dt: Type) =>
|
||||
T.ensureTypeMatches(expr.iterable, ArrayType(dt), dt).as(Option.empty[Type])
|
||||
case _ => Free.pure[Alg, Option[Type]](None)
|
||||
},
|
||||
(_: Unit, ops: Model) =>
|
||||
// TODO streams should escape the scope
|
||||
N.endScope() as (ops match {
|
||||
case op: FuncOp =>
|
||||
(stOpt: Option[Type], ops: Model) =>
|
||||
N.endScope() as ((stOpt, ops) match {
|
||||
case (Some(t), op: FuncOp) =>
|
||||
FuncOp.wrap(
|
||||
ForTag(expr.item.value, ValuesAlgebra.valueToModel(expr.iterable)),
|
||||
ForTag(expr.item.value, ValuesAlgebra.valueToModel(expr.iterable, t)),
|
||||
FuncOp.node(
|
||||
expr.par.fold[OpTag](SeqTag)(_ => ParTag),
|
||||
Chain(op, FuncOp.leaf(NextTag(expr.item.value)))
|
||||
|
@ -16,6 +16,7 @@ import cats.data.Chain
|
||||
import cats.free.Free
|
||||
import cats.syntax.flatMap._
|
||||
import cats.syntax.functor._
|
||||
import cats.syntax.apply._
|
||||
|
||||
class FuncSem[F[_]](val expr: FuncExpr[F]) extends AnyVal {
|
||||
import expr._
|
||||
@ -61,41 +62,43 @@ class FuncSem[F[_]](val expr: FuncExpr[F]) extends AnyVal {
|
||||
// Check return value type
|
||||
((funcArrow.res, retValue) match {
|
||||
case (Some(t), Some(v)) =>
|
||||
V.resolveType(v).flatMap {
|
||||
V.resolveType(v).flatTap {
|
||||
case Some(vt) => T.ensureTypeMatches(v, t, vt).void
|
||||
case None => Free.pure[Alg, Unit](())
|
||||
}
|
||||
case _ =>
|
||||
Free.pure[Alg, Unit](())
|
||||
Free.pure[Alg, Option[Type]](None)
|
||||
|
||||
// Erase arguments and internal variables
|
||||
}) >> A.endScope() >> N.endScope() >> (bodyGen match {
|
||||
case bg: FuncOp if ret.isDefined == retValue.isDefined =>
|
||||
val argNames = args.map(_.name.value)
|
||||
}).flatMap(retType =>
|
||||
A.endScope() >> N.endScope() >> (bodyGen match {
|
||||
case bg: FuncOp if ret.isDefined == retValue.isDefined =>
|
||||
val argNames = args.map(_.name.value)
|
||||
|
||||
val model = FuncModel(
|
||||
name = name.value,
|
||||
args = ArgsDef(
|
||||
argNames
|
||||
.zip(funcArrow.args)
|
||||
.map {
|
||||
case (n, dt: DataType) => ArgDef.Data(n, dt)
|
||||
case (n, at: ArrowType) => ArgDef.Arrow(n, at)
|
||||
}
|
||||
),
|
||||
ret = retValue
|
||||
.map(ValuesAlgebra.valueToModel)
|
||||
.flatMap(vd => funcArrow.res.map(Call.Arg(vd, _))),
|
||||
body = bg
|
||||
)
|
||||
val model = FuncModel(
|
||||
name = name.value,
|
||||
args = ArgsDef(
|
||||
argNames
|
||||
.zip(funcArrow.args)
|
||||
.map {
|
||||
case (n, dt: DataType) => ArgDef.Data(n, dt)
|
||||
case (n, at: ArrowType) => ArgDef.Arrow(n, at)
|
||||
}
|
||||
),
|
||||
ret = (retValue, retType, funcArrow.res).mapN { case (retV, retT, resT) =>
|
||||
ValuesAlgebra.valueToModel(retV, retT) -> resT
|
||||
},
|
||||
body = bg
|
||||
)
|
||||
|
||||
N.defineArrow(
|
||||
name,
|
||||
funcArrow,
|
||||
isRoot = true
|
||||
) as model
|
||||
case m => Free.pure[Alg, Model](Model.error("Function body is not a funcOp, it's " + m))
|
||||
})
|
||||
N.defineArrow(
|
||||
name,
|
||||
funcArrow,
|
||||
isRoot = true
|
||||
) as model
|
||||
case m => Free.pure[Alg, Model](Model.error("Function body is not a funcOp, it's " + m))
|
||||
})
|
||||
)
|
||||
|
||||
def program[Alg[_]](implicit
|
||||
T: TypesAlgebra[F, Alg],
|
||||
|
@ -6,6 +6,7 @@ import aqua.parser.expr.IfExpr
|
||||
import aqua.semantics.rules.ValuesAlgebra
|
||||
import aqua.semantics.rules.types.TypesAlgebra
|
||||
import aqua.semantics.Prog
|
||||
import aqua.types.Type
|
||||
import cats.free.Free
|
||||
import cats.syntax.functor._
|
||||
|
||||
@ -21,26 +22,30 @@ class IfSem[F[_]](val expr: IfExpr[F]) extends AnyVal {
|
||||
V.resolveType(expr.right).flatMap {
|
||||
case Some(rt) =>
|
||||
T.ensureTypeMatches(expr.right, lt, rt)
|
||||
.map(m => Some(lt -> rt).filter(_ => m))
|
||||
case None =>
|
||||
Free.pure[Alg, Boolean](false)
|
||||
Free.pure[Alg, Option[(Type, Type)]](None)
|
||||
}
|
||||
case None =>
|
||||
V.resolveType(expr.right).as(false)
|
||||
V.resolveType(expr.right).as[Option[(Type, Type)]](None)
|
||||
},
|
||||
(r: Boolean, ops: Model) =>
|
||||
ops match {
|
||||
case op: FuncOp if r =>
|
||||
Free.pure[Alg, Model](
|
||||
FuncOp.wrap(
|
||||
MatchMismatchTag(
|
||||
ValuesAlgebra.valueToModel(expr.left),
|
||||
ValuesAlgebra.valueToModel(expr.right),
|
||||
expr.eqOp.value
|
||||
),
|
||||
op
|
||||
)
|
||||
)
|
||||
case _ => Free.pure[Alg, Model](Model.error("If expression errored"))
|
||||
(r: Option[(Type, Type)], ops: Model) =>
|
||||
r.fold(Free.pure[Alg, Model](Model.error("If expression errored in matching types"))) {
|
||||
case (lt, rt) =>
|
||||
ops match {
|
||||
case op: FuncOp =>
|
||||
Free.pure[Alg, Model](
|
||||
FuncOp.wrap(
|
||||
MatchMismatchTag(
|
||||
ValuesAlgebra.valueToModel(expr.left, lt),
|
||||
ValuesAlgebra.valueToModel(expr.right, rt),
|
||||
expr.eqOp.value
|
||||
),
|
||||
op
|
||||
)
|
||||
)
|
||||
case _ => Free.pure[Alg, Model](Model.error("Wrong body of the if expression"))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import aqua.parser.expr.OnExpr
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.ValuesAlgebra
|
||||
import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
||||
import aqua.types.ScalarType
|
||||
import cats.data.Chain
|
||||
import cats.syntax.flatMap._
|
||||
import cats.syntax.functor._
|
||||
@ -28,8 +29,8 @@ class OnSem[F[_]](val expr: OnExpr[F]) extends AnyVal {
|
||||
case op: FuncOp =>
|
||||
FuncOp.wrap(
|
||||
OnTag(
|
||||
ValuesAlgebra.valueToModel(expr.peerId),
|
||||
Chain.fromSeq(expr.via).map(ValuesAlgebra.valueToModel)
|
||||
ValuesAlgebra.valueToModel(expr.peerId, ScalarType.string),
|
||||
Chain.fromSeq(expr.via).map(ValuesAlgebra.valueToModel(_, ScalarType.string))
|
||||
),
|
||||
op
|
||||
)
|
||||
|
@ -4,7 +4,7 @@ import aqua.model._
|
||||
import aqua.parser.lexer._
|
||||
import aqua.semantics.rules.names.NamesAlgebra
|
||||
import aqua.semantics.rules.types.TypesAlgebra
|
||||
import aqua.types.{ArrowType, LiteralType, Type}
|
||||
import aqua.types.{ArrowType, LiteralType, StreamType, Type}
|
||||
import cats.data.Chain
|
||||
import cats.free.Free
|
||||
import cats.syntax.apply._
|
||||
@ -82,10 +82,10 @@ object ValuesAlgebra {
|
||||
case (f: IntoField[F]) :: tail => opsToModel(tail).prepend(IntoFieldModel(f.value))
|
||||
}
|
||||
|
||||
def valueToModel[F[_]](v: Value[F]): ValueModel =
|
||||
def valueToModel[F[_]](v: Value[F], t: Type): ValueModel =
|
||||
v match {
|
||||
case l: Literal[F] => LiteralModel(l.value)
|
||||
case VarLambda(name, ops) => VarModel(name.value, opsToModel(ops))
|
||||
case VarLambda(name, ops) => VarModel(name.value, t, opsToModel(ops))
|
||||
}
|
||||
|
||||
implicit def deriveValuesAlgebra[F[_], Alg[_]](implicit
|
||||
|
@ -5,7 +5,7 @@ import aqua.types.{ArrowType, Type}
|
||||
|
||||
sealed trait NameOp[F[_], T]
|
||||
|
||||
case class ReadName[F[_]](name: Name[F]) extends NameOp[F, Option[Type]]
|
||||
case class ReadName[F[_]](name: Name[F], mustBeDefined: Boolean) extends NameOp[F, Option[Type]]
|
||||
case class ConstantDefined[F[_]](name: Name[F]) extends NameOp[F, Option[Type]]
|
||||
|
||||
case class ReadArrow[F[_]](name: Name[F]) extends NameOp[F, Option[ArrowType]]
|
||||
|
@ -7,9 +7,10 @@ import cats.free.Free
|
||||
|
||||
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 read(name: Name[F], mustBeDefined: Boolean = true): Free[Alg, Option[Type]] =
|
||||
Free.liftInject[Alg](ReadName(name, mustBeDefined))
|
||||
|
||||
// TODO can be implemented via read?
|
||||
def constantDefined(name: Name[F]): Free[Alg, Option[Type]] =
|
||||
Free.liftInject[Alg](ConstantDefined(name))
|
||||
|
||||
|
@ -37,11 +37,11 @@ class NamesInterpreter[F[_], X](implicit lens: Lens[X, NamesState[F]], error: Re
|
||||
.orElseF(readName(rn.name.value))
|
||||
.value
|
||||
.flatTap {
|
||||
case Some(_) => State.pure(())
|
||||
case None =>
|
||||
case None if rn.mustBeDefined =>
|
||||
getState.flatMap(st =>
|
||||
report(rn.name, "Undefined name, available: " + st.allNames.mkString(", "))
|
||||
)
|
||||
case _ => State.pure(())
|
||||
}
|
||||
case rn: ConstantDefined[F] =>
|
||||
constantDefined(rn.name.value)
|
||||
|
@ -99,6 +99,7 @@ class TypesInterpreter[F[_], X](implicit lens: Lens[X, TypesState[F]], error: Re
|
||||
}
|
||||
|
||||
case etm: EnsureTypeMatches[F] =>
|
||||
// TODO in case of two literals, check for types intersection?
|
||||
if (etm.expected.acceptsValueOf(etm.`given`)) State.pure(true)
|
||||
else
|
||||
report(etm.token, s"Types mismatch, expected: ${etm.expected}, given: ${etm.`given`}")
|
||||
|
@ -9,10 +9,11 @@ import aqua.parser.lexer.{
|
||||
IntoField,
|
||||
LambdaOp,
|
||||
Name,
|
||||
StreamTypeToken,
|
||||
Token,
|
||||
TypeToken
|
||||
}
|
||||
import aqua.types.{ArrayType, ArrowType, DataType, ProductType, Type}
|
||||
import aqua.types.{ArrayType, ArrowType, DataType, ProductType, StreamType, Type}
|
||||
import cats.data.Validated.{Invalid, Valid}
|
||||
import cats.data.{Chain, NonEmptyChain, ValidatedNec}
|
||||
import cats.kernel.Monoid
|
||||
@ -30,6 +31,10 @@ case class TypesState[F[_]](
|
||||
resolveTypeToken(dtt).collect { case it: DataType =>
|
||||
ArrayType(it)
|
||||
}
|
||||
case StreamTypeToken(_, dtt) =>
|
||||
resolveTypeToken(dtt).collect { case it: DataType =>
|
||||
StreamType(it)
|
||||
}
|
||||
case ctt: CustomTypeToken[F] => strict.get(ctt.value)
|
||||
case btt: BasicTypeToken[F] => Some(btt.value)
|
||||
case ArrowTypeToken(_, args, res) =>
|
||||
|
@ -73,15 +73,9 @@ case class ProductType(name: String, fields: NonEmptyMap[String, Type]) extends
|
||||
s"$name{${fields.map(_.toString).toNel.toList.map(kv => kv._1 + ": " + kv._2).mkString(", ")}}"
|
||||
}
|
||||
|
||||
sealed trait CallableType extends Type {
|
||||
def acceptsAsArguments(valueTypes: List[Type]): Boolean
|
||||
def args: List[Type]
|
||||
def res: Option[Type]
|
||||
}
|
||||
case class ArrowType(args: List[Type], res: Option[Type]) extends Type {
|
||||
|
||||
case class ArrowType(args: List[Type], res: Option[Type]) extends CallableType {
|
||||
|
||||
override def acceptsAsArguments(valueTypes: List[Type]): Boolean =
|
||||
def acceptsAsArguments(valueTypes: List[Type]): Boolean =
|
||||
(args.length == valueTypes.length) && args
|
||||
.zip(valueTypes)
|
||||
.forall(av => av._1.acceptsValueOf(av._2))
|
||||
@ -90,6 +84,8 @@ case class ArrowType(args: List[Type], res: Option[Type]) extends CallableType {
|
||||
args.map(_.toString).mkString(", ") + " -> " + res.map(_.toString).getOrElse("()")
|
||||
}
|
||||
|
||||
case class StreamType(element: Type) extends DataType
|
||||
|
||||
object Type {
|
||||
import Double.NaN
|
||||
|
||||
@ -132,9 +128,11 @@ object Type {
|
||||
case (x: ScalarType, LiteralType(ys, _)) if ys == Set(x) => 0.0
|
||||
case (x: ScalarType, LiteralType(ys, _)) if ys(x) => 1.0
|
||||
case (x: ArrayType, y: ArrayType) => cmp(x.element, y.element)
|
||||
case (x: ArrayType, y: StreamType) => cmp(x.element, y.element)
|
||||
case (x: StreamType, y: StreamType) => cmp(x.element, y.element)
|
||||
case (ProductType(_, xFields), ProductType(_, yFields)) =>
|
||||
cmpProd(xFields, yFields)
|
||||
case (l: CallableType, r: CallableType) =>
|
||||
case (l: ArrowType, r: ArrowType) =>
|
||||
val argL = l.args
|
||||
val resL = l.res
|
||||
val argR = r.args
|
||||
|
@ -94,4 +94,13 @@ class TypeSpec extends AnyFlatSpec with Matchers {
|
||||
accepts(four, one) should be(false)
|
||||
}
|
||||
|
||||
"streams" should "be accepted as an array, but not vice versa" in {
|
||||
val stream: Type = StreamType(bool)
|
||||
val array: Type = ArrayType(bool)
|
||||
|
||||
accepts(array, stream) should be(true)
|
||||
accepts(stream, array) should be(false)
|
||||
accepts(stream, stream) should be(true)
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user