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:
Dmitry Kurinskiy 2021-04-27 16:59:12 +03:00 committed by GitHub
parent 3b3ff24133
commit 27f2912c5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 339 additions and 195 deletions

View File

@ -12,6 +12,7 @@ data User:
service Test("test"): service Test("test"):
getUserList: -> []User getUserList: -> []User
doSomething: -> bool doSomething: -> bool
check: bool -> ()
func initAfterJoin(me: User) -> []User: func initAfterJoin(me: User) -> []User:
allUsers <- Test.getUserList() allUsers <- Test.getUserList()
@ -26,3 +27,18 @@ func initAfterJoin(me: User) -> []User:
Op.identity() Op.identity()
par Test.doSomething() par Test.doSomething()
<- allUsers <- 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

View File

@ -3,6 +3,7 @@ package aqua.backend.air
import aqua.model._ import aqua.model._
import aqua.model.func.Call import aqua.model.func.Call
import aqua.model.func.body._ import aqua.model.func.body._
import aqua.types.StreamType
import cats.Eval import cats.Eval
import cats.data.Chain import cats.data.Chain
import cats.free.Cofree import cats.free.Cofree
@ -24,9 +25,13 @@ object AirGen {
def valueToData(vm: ValueModel): DataView = vm match { def valueToData(vm: ValueModel): DataView = vm match {
case LiteralModel(value) => DataView.StringScalar(value) case LiteralModel(value) => DataView.StringScalar(value)
case VarModel(name, lambda) => case VarModel(name, t, lambda) =>
if (lambda.isEmpty) DataView.Variable(name) val n = t match {
else DataView.VarLens(name, lambdaToString(lambda.toList)) 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 { def opsToSingle(ops: Chain[AirGen]): AirGen = ops.toList match {
@ -76,8 +81,11 @@ object AirGen {
peerId.map(valueToData).getOrElse(DataView.InitPeerId), peerId.map(valueToData).getOrElse(DataView.InitPeerId),
valueToData(serviceId), valueToData(serviceId),
funcName, funcName,
args.map(_.model).map(valueToData), args.map(valueToData),
exportTo exportTo.map {
case Call.Export(name, _: StreamType) => "$" + name
case Call.Export(name, _) => name
}
) )
) )

View File

@ -39,7 +39,7 @@ case class TypescriptFunc(func: FuncCallable) {
}.mkString("\n") }.mkString("\n")
val retType = func.ret val retType = func.ret
.map(_.`type`) .map(_._2)
.fold("void")(typeToTs) .fold("void")(typeToTs)
val returnVal = val returnVal =
@ -90,6 +90,7 @@ object TypescriptFunc {
def typeToTs(t: Type): String = t match { def typeToTs(t: Type): String = t match {
case ArrayType(t) => typeToTs(t) + "[]" case ArrayType(t) => typeToTs(t) + "[]"
case StreamType(t) => typeToTs(t) + "[]"
case pt: ProductType => case pt: ProductType =>
s"{${pt.fields.map(typeToTs).toNel.map(kv => kv._1 + ":" + kv._2).toList.mkString(";")}}" s"{${pt.fields.map(typeToTs).toNel.map(kv => kv._1 + ":" + kv._2).toList.mkString(";")}}"
case st: ScalarType if ScalarType.number(st) => "number" case st: ScalarType if ScalarType.number(st) => "number"

View File

@ -1,5 +1,6 @@
package aqua.model package aqua.model
import aqua.types.Type
import cats.data.Chain import cats.data.Chain
sealed trait ValueModel { sealed trait ValueModel {
@ -16,7 +17,8 @@ sealed trait LambdaModel
case object IntoArrayModel extends LambdaModel case object IntoArrayModel extends LambdaModel
case class IntoFieldModel(field: String) 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) def deriveFrom(vm: VarModel): VarModel = vm.copy(lambda = vm.lambda ++ lambda)
override def resolveWith(map: Map[String, ValueModel]): ValueModel = { 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) res <- two(variable)
<- 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 // it couldn't go to a cycle as long as the semantics protects it
case _ => n.resolveWith(map) case _ => n.resolveWith(map)
} }

View File

@ -9,20 +9,19 @@ import cats.syntax.functor._
* @param args Argument definitions * @param args Argument definitions
* @param callWith Values provided for arguments * @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) // 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) // 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] = lazy val dataArgs: Map[String, ValueModel] =
zipped.collect { case (ArgDef.Data(name, _), Call.Arg(value, _)) => zipped.collect { case (ArgDef.Data(name, _), value) =>
name -> value name -> value
}.toMap }.toMap
def arrowArgs(arrowsInScope: Map[String, FuncCallable]): Map[String, FuncCallable] = def arrowArgs(arrowsInScope: Map[String, FuncCallable]): Map[String, FuncCallable] =
zipped.collect { zipped.collect {
case (ArgDef.Arrow(name, _), Call.Arg(VarModel(value, _), _)) case (ArgDef.Arrow(name, _), VarModel(value, _, _)) if arrowsInScope.contains(value) =>
if arrowsInScope.contains(value) =>
name -> arrowsInScope(value) name -> arrowsInScope(value)
}.toMap }.toMap
} }
@ -33,7 +32,7 @@ object ArgsCall {
arrow: ArrowType, arrow: ArrowType,
argPrefix: String = "arg", argPrefix: String = "arg",
retName: String = "init_call_res" 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 argNamesTypes = arrow.args.zipWithIndex.map(iv => iv.map(i => argPrefix + i).swap)
val argsDef = ArgsDef(argNamesTypes.map { val argsDef = ArgsDef(argNamesTypes.map {
@ -43,12 +42,12 @@ object ArgsCall {
val call = Call( val call = Call(
argNamesTypes.map { case (a, t) => 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)))
} }
} }

View File

@ -11,10 +11,10 @@ case class ArgsDef(args: List[ArgDef]) {
def types: List[Type] = args.map(_.`type`) 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, _) => lazy val dataArgs: Chain[ArgDef.Data] = Chain.fromSeq(args.collect { case ad: ArgDef.Data =>
n ad
}) })
lazy val arrowArgs: Chain[ArgDef.Arrow] = Chain.fromSeq(args.collect { case ad: ArgDef.Arrow => lazy val arrowArgs: Chain[ArgDef.Arrow] = Chain.fromSeq(args.collect { case ad: ArgDef.Arrow =>

View File

@ -1,24 +1,24 @@
package aqua.model.func package aqua.model.func
import aqua.model.ValueModel import aqua.model.{ValueModel, VarModel}
import aqua.types.Type 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 = def mapValues(f: ValueModel => ValueModel): Call =
Call( Call(
args.map(_.mapValues(f)), args.map(f),
exportTo 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 { 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 = def model: ValueModel = VarModel(name, `type`)
copy(f(model))
} }
} }

View File

@ -2,7 +2,7 @@ package aqua.model.func
import aqua.model.func.body.{CallArrowTag, FuncOp, OpTag} import aqua.model.func.body.{CallArrowTag, FuncOp, OpTag}
import aqua.model.{ValueModel, VarModel} import aqua.model.{ValueModel, VarModel}
import aqua.types.ArrowType import aqua.types.{ArrowType, DataType, Type}
import cats.Eval import cats.Eval
import cats.data.Chain import cats.data.Chain
import cats.free.Cofree import cats.free.Cofree
@ -11,7 +11,7 @@ case class FuncCallable(
funcName: String, funcName: String,
body: FuncOp, body: FuncOp,
args: ArgsDef, args: ArgsDef,
ret: Option[Call.Arg], ret: Option[(ValueModel, Type)],
capturedArrows: Map[String, FuncCallable], capturedArrows: Map[String, FuncCallable],
capturedValues: Map[String, ValueModel] capturedValues: Map[String, ValueModel]
) { ) {
@ -19,7 +19,7 @@ case class FuncCallable(
def arrowType: ArrowType = def arrowType: ArrowType =
ArrowType( ArrowType(
args.types, args.types,
ret.map(_.`type`) ret.map(_._2)
) )
def findNewNames(forbidden: Set[String], introduce: Set[String]): Map[String, String] = def findNewNames(forbidden: Set[String], introduce: Set[String]): Map[String, String] =
@ -52,7 +52,7 @@ case class FuncCallable(
val treeWithValues = body.resolveValues(argsToData) val treeWithValues = body.resolveValues(argsToData)
// Function body on its own defines some values; collect their names // 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 // We have some names in scope (forbiddenNames), can't introduce them again; so find new names
val shouldRename = findNewNames(forbiddenNames, treeDefines) val shouldRename = findNewNames(forbiddenNames, treeDefines)
@ -61,7 +61,7 @@ case class FuncCallable(
if (shouldRename.isEmpty) treeWithValues else treeWithValues.rename(shouldRename) if (shouldRename.isEmpty) treeWithValues else treeWithValues.rename(shouldRename)
// Result could be derived from arguments, or renamed; take care about that // 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: VarModel if shouldRename.contains(v.name) => v.copy(shouldRename(v.name))
case v => v case v => v
} }
@ -79,8 +79,8 @@ case class FuncCallable(
case ((noNames, resolvedExports), CallArrowTag(fn, c)) if allArrows.contains(fn) => case ((noNames, resolvedExports), CallArrowTag(fn, c)) if allArrows.contains(fn) =>
// Apply arguments to a function recursion // Apply arguments to a function recursion
val callResolved = c.mapValues(_.resolveWith(resolvedExports)) val callResolved = c.mapValues(_.resolveWith(resolvedExports))
val possibleArrowNames = callResolved.args.collect { val possibleArrowNames = callResolved.args.collect { case VarModel(m, _: ArrowType, _) =>
case Call.Arg(VarModel(m, _), _: ArrowType) => m m
}.toSet }.toSet
val (appliedOp, value) = val (appliedOp, value) =
@ -92,7 +92,10 @@ case class FuncCallable(
// TODO: actually it's done and dropped so keep and pass it instead // TODO: actually it's done and dropped so keep and pass it instead
val newNames = appliedOp.definesValueNames.value val newNames = appliedOp.definesValueNames.value
// At the very end, will need to resolve what is used as results with the result values // 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) => case (acc @ (_, resolvedExports), tag) =>
tag match { tag match {
case CallArrowTag(fn, _) if !allArrows.contains(fn) => case CallArrowTag(fn, _) if !allArrows.contains(fn) =>

View File

@ -2,11 +2,12 @@ package aqua.model.func
import aqua.model.func.body.FuncOp import aqua.model.func.body.FuncOp
import aqua.model.{Model, ValueModel} import aqua.model.{Model, ValueModel}
import aqua.types.Type
case class FuncModel( case class FuncModel(
name: String, name: String,
args: ArgsDef, args: ArgsDef,
ret: Option[Call.Arg], ret: Option[(ValueModel, Type)],
body: FuncOp body: FuncOp
) extends Model { ) extends Model {

View File

@ -23,9 +23,9 @@ case class FuncOp(tree: Cofree[Chain, OpTag]) extends Model {
def definesValueNames: Eval[Set[String]] = cata[Set[String]] { def definesValueNames: Eval[Set[String]] = cata[Set[String]] {
case (CallArrowTag(_, Call(_, Some(export))), acc) => 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) => 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 (NextTag(export), acc) => Eval.later(acc.foldLeft(Set(export))(_ ++ _))
case (_, acc) => Eval.later(acc.foldLeft(Set.empty[String])(_ ++ _)) case (_, acc) => Eval.later(acc.foldLeft(Set.empty[String])(_ ++ _))
} }

View File

@ -3,23 +3,25 @@ package aqua.model.transform
import aqua.model.ValueModel import aqua.model.ValueModel
import aqua.model.func.Call import aqua.model.func.Call
import aqua.model.func.body.{FuncOp, FuncOps} import aqua.model.func.body.{FuncOp, FuncOps}
import aqua.types.DataType
trait ArgsProvider { trait ArgsProvider {
def transform(op: FuncOp): FuncOp 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( FuncOps.callService(
dataServiceId, dataServiceId,
name, name,
Call(Nil, Some(name)) Call(Nil, Some(Call.Export(name, t)))
) )
def transform(op: FuncOp): FuncOp = def transform(op: FuncOp): FuncOp =
FuncOps.seq( FuncOps.seq(
names.map(getDataOp) :+ op: _* names.map((getDataOp _).tupled) :+ op: _*
) )
} }

View File

@ -3,7 +3,6 @@ package aqua.model.transform
import aqua.model.{LiteralModel, ValueModel} import aqua.model.{LiteralModel, ValueModel}
import aqua.model.func.Call import aqua.model.func.Call
import aqua.model.func.body.{FuncOp, FuncOps} import aqua.model.func.body.{FuncOp, FuncOps}
import aqua.types.ScalarType.string
case class ErrorsCatcher( case class ErrorsCatcher(
enabled: Boolean, enabled: Boolean,
@ -28,7 +27,7 @@ case class ErrorsCatcher(
object ErrorsCatcher { object ErrorsCatcher {
// TODO not a string // TODO not a string
val lastErrorArg: Call.Arg = Call.Arg(LiteralModel("%last_error%"), string) val lastErrorArg: ValueModel = LiteralModel("%last_error%")
val lastErrorCall: Call = Call( val lastErrorCall: Call = Call(
lastErrorArg :: Nil, lastErrorArg :: Nil,

View File

@ -5,6 +5,7 @@ import aqua.model.func.{ArgDef, ArgsCall, ArgsDef, Call, FuncCallable}
import aqua.model.func.body.{FuncOp, FuncOps} import aqua.model.func.body.{FuncOp, FuncOps}
import aqua.types.ArrowType import aqua.types.ArrowType
import cats.Eval import cats.Eval
import cats.syntax.apply._
case class ResolveFunc( case class ResolveFunc(
transform: FuncOp => FuncOp, transform: FuncOp => FuncOp,
@ -14,11 +15,11 @@ case class ResolveFunc(
arrowCallbackPrefix: String = "init_peer_callable_" 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( callback(
respFuncName, respFuncName,
Call( Call(
retArg :: Nil, retModel :: Nil,
None None
) )
) )
@ -30,7 +31,7 @@ case class ResolveFunc(
arrowCallbackPrefix + name, arrowCallbackPrefix + name,
callback(name, call), callback(name, call),
args, args,
ret, (ret.map(_.model), arrowType.res).mapN(_ -> _),
Map.empty, Map.empty,
Map.empty Map.empty
) )
@ -63,7 +64,7 @@ case class ResolveFunc(
def resolve(func: FuncCallable, funcArgName: String = "_func"): Eval[FuncOp] = def resolve(func: FuncCallable, funcArgName: String = "_func"): Eval[FuncOp] =
wrap(func) wrap(func)
.resolve( .resolve(
Call(Call.Arg(VarModel(funcArgName), func.arrowType) :: Nil, None), Call(VarModel(funcArgName, func.arrowType) :: Nil, None),
Map(funcArgName -> func), Map(funcArgName -> func),
Set.empty Set.empty
) )

View File

@ -3,6 +3,7 @@ package aqua.model.transform
import aqua.model.func.body._ import aqua.model.func.body._
import aqua.model.func.FuncCallable import aqua.model.func.FuncCallable
import aqua.model.VarModel import aqua.model.VarModel
import aqua.types.ScalarType
import cats.data.Chain import cats.data.Chain
import cats.free.Cofree import cats.free.Cofree
@ -10,7 +11,7 @@ object Transform {
def forClient(func: FuncCallable, conf: BodyConfig): Cofree[Chain, OpTag] = { def forClient(func: FuncCallable, conf: BodyConfig): Cofree[Chain, OpTag] = {
val initCallable: InitPeerCallable = InitViaRelayCallable( val initCallable: InitPeerCallable = InitViaRelayCallable(
Chain.one(VarModel(conf.relayVarName)) Chain.one(VarModel(conf.relayVarName, ScalarType.string))
) )
val errorsCatcher = ErrorsCatcher( val errorsCatcher = ErrorsCatcher(
enabled = conf.wrapWithXor, enabled = conf.wrapWithXor,
@ -19,7 +20,12 @@ object Transform {
initCallable initCallable
) )
val argsProvider: ArgsProvider = 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 = val transform =
errorsCatcher.transform _ compose initCallable.transform compose argsProvider.transform errorsCatcher.transform _ compose initCallable.transform compose argsProvider.transform

View File

@ -1,16 +1,7 @@
package aqua.model package aqua.model
import aqua.model.func.Call import aqua.model.func.Call
import aqua.model.func.body.{ import aqua.model.func.body._
CallServiceTag,
FuncOp,
FuncOps,
MatchMismatchTag,
OnTag,
OpTag,
SeqTag,
XorTag
}
import aqua.model.transform.BodyConfig import aqua.model.transform.BodyConfig
import aqua.types.ScalarType import aqua.types.ScalarType
import cats.Eval import cats.Eval
@ -30,10 +21,9 @@ case class Node(tag: OpTag, ops: List[Node] = Nil) {
else else
Console.BLUE + left + Console.RED + " != " + Console.YELLOW + right) 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 + "(" + Console.GREEN + "(" +
equalOrNot(left.model, right.model) + Console.GREEN + ", " + equalOrNot(left, right) + Console.GREEN + ")"
equalOrNot(left.`type`, right.`type`) + Console.GREEN + ")"
private def diffCall(left: Call, right: Call): String = private def diffCall(left: Call, right: Call): String =
if (left == right) Console.GREEN + left + Console.RESET 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)))) Cofree(tree.tag, Eval.later(Chain.fromSeq(tree.ops.map(nodeToCof))))
val relay = LiteralModel("relay") val relay = LiteralModel("relay")
val relayV = VarModel("relay") val relayV = VarModel("relay", ScalarType.string)
val initPeer = LiteralModel.initPeerId val initPeer = LiteralModel.initPeerId
val emptyCall = Call(Nil, None) val emptyCall = Call(Nil, None)
val otherPeer = LiteralModel("other-peer") val otherPeer = LiteralModel("other-peer")
@ -111,7 +101,7 @@ object Node {
CallServiceTag( CallServiceTag(
bc.errorHandlingCallback, bc.errorHandlingCallback,
bc.errorFuncName, bc.errorFuncName,
Call(Call.Arg(LiteralModel("%last_error%"), ScalarType.string) :: Nil, None), Call(LiteralModel("%last_error%") :: Nil, None),
Option(on) Option(on)
) )
) )
@ -120,7 +110,7 @@ object Node {
CallServiceTag( CallServiceTag(
bc.callbackSrvId, bc.callbackSrvId,
bc.respFuncName, bc.respFuncName,
Call(Call.Arg(value, ScalarType.string) :: Nil, None), Call(value :: Nil, None),
Option(on) Option(on)
) )
) )
@ -129,7 +119,7 @@ object Node {
CallServiceTag( CallServiceTag(
bc.dataSrvId, bc.dataSrvId,
name, name,
Call(Nil, Some(name)), Call(Nil, Some(Call.Export(name, ScalarType.string))),
Option(on) Option(on)
) )
) )

View File

@ -19,7 +19,7 @@ class TransformSpec extends AnyFlatSpec with Matchers {
"ret", "ret",
FuncOp(on(otherPeer, Nil, call(1))), FuncOp(on(otherPeer, Nil, call(1))),
ArgsDef.empty, ArgsDef.empty,
Some(Call.Arg(ret, ScalarType.string)), Some((ret, ScalarType.string)),
Map.empty, Map.empty,
Map.empty Map.empty
) )
@ -57,7 +57,7 @@ class TransformSpec extends AnyFlatSpec with Matchers {
"ret", "ret",
FuncOp(seq(call(0), on(otherPeer, Nil, call(1)))), FuncOp(seq(call(0), on(otherPeer, Nil, call(1)))),
ArgsDef.empty, ArgsDef.empty,
Some(Call.Arg(ret, ScalarType.string)), Some((ret, ScalarType.string)),
Map.empty, Map.empty,
Map.empty Map.empty
) )
@ -104,9 +104,18 @@ class TransformSpec extends AnyFlatSpec with Matchers {
val f1: FuncCallable = val f1: FuncCallable =
FuncCallable( FuncCallable(
"f1", "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, ArgsDef.empty,
Some(Call.Arg(VarModel("v"), ScalarType.string)), Some((VarModel("v", ScalarType.string), ScalarType.string)),
Map.empty, Map.empty,
Map.empty Map.empty
) )
@ -115,10 +124,10 @@ class TransformSpec extends AnyFlatSpec with Matchers {
FuncCallable( FuncCallable(
"f2", "f2",
FuncOp( FuncOp(
Node(CallArrowTag("callable", Call(Nil, Some("v")))) Node(CallArrowTag("callable", Call(Nil, Some(Call.Export("v", ScalarType.string)))))
), ),
ArgsDef.empty, ArgsDef.empty,
Some(Call.Arg(VarModel("v"), ScalarType.string)), Some((VarModel("v", ScalarType.string), ScalarType.string)),
Map("callable" -> f1), Map("callable" -> f1),
Map.empty Map.empty
) )
@ -134,12 +143,17 @@ class TransformSpec extends AnyFlatSpec with Matchers {
seq( seq(
dataCall(bc, "relay", initPeer), dataCall(bc, "relay", initPeer),
Node( 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( on(
initPeer, initPeer,
relayV :: Nil, relayV :: Nil,
respCall(bc, VarModel("v"), initPeer) respCall(bc, VarModel("v", ScalarType.string), initPeer)
) )
) )
) )

View File

@ -12,7 +12,8 @@ case class ArrowTypeExpr[F[_]](name: Name[F], `type`: ArrowTypeToken[F]) extends
object ArrowTypeExpr extends Expr.Leaf { object ArrowTypeExpr extends Expr.Leaf {
override def p[F[_]: LiftParser: Comonad]: Parser[ArrowTypeExpr[F]] = 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) => case (name, t) =>
ArrowTypeExpr(name, t) ArrowTypeExpr(name, t)
} }

View File

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

View File

@ -12,7 +12,7 @@ case class FieldTypeExpr[F[_]](name: Name[F], `type`: DataTypeToken[F]) extends
object FieldTypeExpr extends Expr.Leaf { object FieldTypeExpr extends Expr.Leaf {
override def p[F[_]: LiftParser: Comonad]: Parser[FieldTypeExpr[F]] = override def p[F[_]: LiftParser: Comonad]: Parser[FieldTypeExpr[F]] =
((Name.p[F] <* ` : `) ~ DataTypeToken.`datatypedef`[F]).map { ((Name.p[F] <* ` : `) ~ DataTypeToken.`datatypedef`[F]).map { case (name, t) =>
case (name, t) => FieldTypeExpr(name, t) FieldTypeExpr(name, t)
} }
} }

View File

@ -25,7 +25,8 @@ object FuncExpr
ParExpr, ParExpr,
ForExpr, ForExpr,
IfExpr, IfExpr,
ElseOtherwiseExpr ElseOtherwiseExpr,
DeclareStreamExpr
) { ) {
override def p[F[_]: LiftParser: Comonad]: Parser[FuncExpr[F]] = override def p[F[_]: LiftParser: Comonad]: Parser[FuncExpr[F]] =

View File

@ -10,7 +10,7 @@ case class Arg[F[_]](name: Name[F], `type`: TypeToken[F])
object Arg { object Arg {
def p[F[_]: LiftParser: Comonad]: P[Arg[F]] = def p[F[_]: LiftParser: Comonad]: P[Arg[F]] =
((Name.p[F] <* ` : `) ~ TypeToken.`typedef`[F]).map { ((Name.p[F] <* ` : `) ~ TypeToken.`typedef`[F]).map { case (name, t) =>
case (name, t) => Arg(name, t) Arg(name, t)
} }
} }

View File

@ -43,9 +43,7 @@ object Token {
val `:` : P[Unit] = P.char(':') val `:` : P[Unit] = P.char(':')
val ` : ` : P[Unit] = P.char(':').surroundedBy(` `.?) val ` : ` : P[Unit] = P.char(':').surroundedBy(` `.?)
val `name`: P[String] = (P.charIn(az) ~ P.charsWhile(anum_).?).map { case (c, s) val `name`: P[String] = (P.charIn(az) ~ P.charsWhile(anum_).?).string
c.toString ++ s.getOrElse("")
}
val `Class`: P[String] = (P.charIn(AZ) ~ P.charsWhile(anum_).?).map { case (c, s) val `Class`: P[String] = (P.charIn(AZ) ~ P.charsWhile(anum_).?).map { case (c, s)
c.toString ++ s.getOrElse("") 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.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] = ` `.?.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("->") <* ` `.?
val ` <- ` : P[Unit] = (` `.?.with1 *> P.string("<-") <* ` `.?).backtrack val ` <- ` : P[Unit] = (` `.?.with1 *> P.string("<-") <* ` `.?).backtrack
val `=` : P[Unit] = P.string("=") val `=` : P[Unit] = P.string("=")

View File

@ -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) 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] { case class CustomTypeToken[F[_]: Comonad](name: F[String]) extends DataTypeToken[F] {
override def as[T](v: T): F[T] = name.as(v) 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]] = def `arrowdef`[F[_]: LiftParser: Comonad]: P[ArrowTypeToken[F]] =
(comma0(DataTypeToken.`datatypedef`).with1 ~ ` -> `.lift ~ (comma0(DataTypeToken.`datatypedef`).with1 ~ ` -> `.lift ~
(DataTypeToken.`datatypedef` (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) 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 { object DataTypeToken {
def `arraytypedef`[F[_]: LiftParser: Comonad]: P[ArrayTypeToken[F]] = 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]] = def `datatypedef`[F[_]: LiftParser: Comonad]: P[DataTypeToken[F]] =
P.oneOf( 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 { object TypeToken {
def `typedef`[F[_]: LiftParser: Comonad]: P[TypeToken[F]] = def `typedef`[F[_]: LiftParser: Comonad]: P[TypeToken[F]] =
P.oneOf(ArrowTypeToken.`arrowdef`.backtrack :: DataTypeToken.`datatypedef` :: Nil) P.oneOf(
ArrowTypeToken.`arrowdef`.backtrack :: DataTypeToken.`datatypedef` :: Nil
)
} }

View File

@ -21,6 +21,7 @@ object ExprSem {
case expr: AbilityIdExpr[F] => new AbilityIdSem(expr).program[G] case expr: AbilityIdExpr[F] => new AbilityIdSem(expr).program[G]
case expr: AliasExpr[F] => new AliasSem(expr).program[G] case expr: AliasExpr[F] => new AliasSem(expr).program[G]
case expr: ConstantExpr[F] => new ConstantSem(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: ArrowTypeExpr[F] => new ArrowTypeSem(expr).program[G]
case expr: CallArrowExpr[F] => new CallArrowSem(expr).program[G] case expr: CallArrowExpr[F] => new CallArrowSem(expr).program[G]
case expr: DataStructExpr[F] => new DataStructSem(expr).program[G] case expr: DataStructExpr[F] => new DataStructSem(expr).program[G]

View File

@ -1,6 +1,6 @@
package aqua.semantics.expr package aqua.semantics.expr
import aqua.model.Model import aqua.model.{Model, ValueModel}
import aqua.model.func.Call import aqua.model.func.Call
import aqua.model.func.body.{CallArrowTag, CallServiceTag, FuncOp} import aqua.model.func.body.{CallArrowTag, CallServiceTag, FuncOp}
import aqua.parser.expr.CallArrowExpr import aqua.parser.expr.CallArrowExpr
@ -9,7 +9,7 @@ import aqua.semantics.rules.ValuesAlgebra
import aqua.semantics.rules.abilities.AbilitiesAlgebra import aqua.semantics.rules.abilities.AbilitiesAlgebra
import aqua.semantics.rules.names.NamesAlgebra import aqua.semantics.rules.names.NamesAlgebra
import aqua.semantics.rules.types.TypesAlgebra import aqua.semantics.rules.types.TypesAlgebra
import aqua.types.ArrowType import aqua.types.{ArrowType, ScalarType, StreamType, Type}
import cats.free.Free import cats.free.Free
import cats.syntax.apply._ import cats.syntax.apply._
import cats.syntax.flatMap._ import cats.syntax.flatMap._
@ -25,16 +25,29 @@ class CallArrowSem[F[_]](val expr: CallArrowExpr[F]) extends AnyVal {
at: ArrowType at: ArrowType
)(implicit )(implicit
N: NamesAlgebra[F, Alg], N: NamesAlgebra[F, Alg],
T: TypesAlgebra[F, Alg],
V: ValuesAlgebra[F, Alg] V: ValuesAlgebra[F, Alg]
): Free[Alg, List[Call.Arg]] = ): Free[Alg, (List[ValueModel], Option[Type])] =
V.checkArguments(expr.funcName, at, args) >> variable V.checkArguments(expr.funcName, at, args) >> variable
.fold(freeUnit[Alg])(exportVar => .fold(freeUnit[Alg].as(Option.empty[Type]))(exportVar =>
at.res.fold( at.res.fold(
// TODO: error! we're trying to export variable, but function has no export type // TODO: error! we're trying to export variable, but function has no export type
freeUnit[Alg] freeUnit[Alg].as(Option.empty[Type])
)(resType => N.define(exportVar, resType).void) )(resType =>
) >> args.foldLeft(Free.pure[Alg, List[Call.Arg]](Nil)) { case (acc, v) => N.read(exportVar, mustBeDefined = false).flatMap {
(acc, V.resolveType(v)).mapN((a, b) => a ++ b.map(Call.Arg(ValuesAlgebra.valueToModel(v), _))) 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 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 Option(at -> sid) // Here we assume that Ability is a Service that must be resolved
case _ => None case _ => None
}.flatMap(_.fold(Free.pure[Alg, Option[FuncOp]](None)) { case (arrowType, serviceId) => }.flatMap(_.fold(Free.pure[Alg, Option[FuncOp]](None)) { case (arrowType, serviceId) =>
checkArgsRes(arrowType) checkArgsRes(arrowType).map { case (argsResolved, t) =>
.map(argsResolved => FuncOp.leaf(
FuncOp.leaf( CallServiceTag(
CallServiceTag( // TODO service id type should not be hardcoded
serviceId = ValuesAlgebra.valueToModel(serviceId), serviceId = ValuesAlgebra.valueToModel(serviceId, ScalarType.string),
funcName = funcName.value, funcName = funcName.value,
Call(argsResolved, variable.map(_.value)) Call(argsResolved, (variable.map(_.value), t).mapN(Call.Export))
)
) )
) )
}
.map(Option(_)) .map(Option(_))
}) })
case None => case None =>
N.readArrow(funcName) N.readArrow(funcName)
.flatMap(_.fold(Free.pure[Alg, Option[FuncOp]](None)) { arrowType => .flatMap(_.fold(Free.pure[Alg, Option[FuncOp]](None)) { arrowType =>
checkArgsRes(arrowType) checkArgsRes(arrowType).map { case (argsResolved, t) =>
.map(argsResolved => FuncOp.leaf(
FuncOp.leaf( CallArrowTag(
CallArrowTag( funcName = funcName.value,
funcName = funcName.value, Call(argsResolved, (variable.map(_.value), t).mapN(Call.Export))
Call(argsResolved, variable.map(_.value))
)
) )
) )
}
.map(Option(_)) .map(Option(_))
}) })
} }
@ -84,6 +96,6 @@ class CallArrowSem[F[_]](val expr: CallArrowExpr[F]) extends AnyVal {
T: TypesAlgebra[F, Alg], T: TypesAlgebra[F, Alg],
V: ValuesAlgebra[F, Alg] V: ValuesAlgebra[F, Alg]
): Prog[Alg, Model] = ): 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")))
} }

View File

@ -34,7 +34,7 @@ class ConstantSem[F[_]](val expr: ConstantExpr[F]) extends AnyVal {
case (_, Some(t), _) => case (_, Some(t), _) =>
N.defineConstant(expr.name, t) as (ConstantModel( N.defineConstant(expr.name, t) as (ConstantModel(
expr.name.value, expr.name.value,
ValuesAlgebra.valueToModel(expr.value) ValuesAlgebra.valueToModel(expr.value, t)
): Model) ): Model)
} }
} yield model } yield model

View File

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

View File

@ -7,7 +7,7 @@ import aqua.semantics.Prog
import aqua.semantics.rules.ValuesAlgebra import aqua.semantics.rules.ValuesAlgebra
import aqua.semantics.rules.names.NamesAlgebra import aqua.semantics.rules.names.NamesAlgebra
import aqua.semantics.rules.types.TypesAlgebra import aqua.semantics.rules.types.TypesAlgebra
import aqua.types.{ArrayType, DataType} import aqua.types.{ArrayType, StreamType, Type}
import cats.data.Chain import cats.data.Chain
import cats.free.Free import cats.free.Free
import cats.syntax.flatMap._ import cats.syntax.flatMap._
@ -21,18 +21,20 @@ class ForSem[F[_]](val expr: ForExpr[F]) extends AnyVal {
T: TypesAlgebra[F, Alg] T: TypesAlgebra[F, Alg]
): Prog[Alg, Model] = ): Prog[Alg, Model] =
Prog.around( Prog.around(
N.beginScope(expr.item) >> V.resolveType(expr.iterable).flatMap { N.beginScope(expr.item) >> V.resolveType(expr.iterable).flatMap[Option[Type]] {
case Some(ArrayType(t)) => case Some(at @ ArrayType(t)) =>
N.define(expr.item, t).void N.define(expr.item, t).as(Option(at))
case Some(dt: DataType) => T.ensureTypeMatches(expr.iterable, ArrayType(dt), dt).void case Some(st @ StreamType(t)) =>
case _ => Free.pure[Alg, Unit](()) 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) => (stOpt: Option[Type], ops: Model) =>
// TODO streams should escape the scope N.endScope() as ((stOpt, ops) match {
N.endScope() as (ops match { case (Some(t), op: FuncOp) =>
case op: FuncOp =>
FuncOp.wrap( FuncOp.wrap(
ForTag(expr.item.value, ValuesAlgebra.valueToModel(expr.iterable)), ForTag(expr.item.value, ValuesAlgebra.valueToModel(expr.iterable, t)),
FuncOp.node( FuncOp.node(
expr.par.fold[OpTag](SeqTag)(_ => ParTag), expr.par.fold[OpTag](SeqTag)(_ => ParTag),
Chain(op, FuncOp.leaf(NextTag(expr.item.value))) Chain(op, FuncOp.leaf(NextTag(expr.item.value)))

View File

@ -16,6 +16,7 @@ import cats.data.Chain
import cats.free.Free import cats.free.Free
import cats.syntax.flatMap._ import cats.syntax.flatMap._
import cats.syntax.functor._ import cats.syntax.functor._
import cats.syntax.apply._
class FuncSem[F[_]](val expr: FuncExpr[F]) extends AnyVal { class FuncSem[F[_]](val expr: FuncExpr[F]) extends AnyVal {
import expr._ import expr._
@ -61,41 +62,43 @@ class FuncSem[F[_]](val expr: FuncExpr[F]) extends AnyVal {
// Check return value type // Check return value type
((funcArrow.res, retValue) match { ((funcArrow.res, retValue) match {
case (Some(t), Some(v)) => case (Some(t), Some(v)) =>
V.resolveType(v).flatMap { V.resolveType(v).flatTap {
case Some(vt) => T.ensureTypeMatches(v, t, vt).void case Some(vt) => T.ensureTypeMatches(v, t, vt).void
case None => Free.pure[Alg, Unit](()) case None => Free.pure[Alg, Unit](())
} }
case _ => case _ =>
Free.pure[Alg, Unit](()) Free.pure[Alg, Option[Type]](None)
// Erase arguments and internal variables // Erase arguments and internal variables
}) >> A.endScope() >> N.endScope() >> (bodyGen match { }).flatMap(retType =>
case bg: FuncOp if ret.isDefined == retValue.isDefined => A.endScope() >> N.endScope() >> (bodyGen match {
val argNames = args.map(_.name.value) case bg: FuncOp if ret.isDefined == retValue.isDefined =>
val argNames = args.map(_.name.value)
val model = FuncModel( val model = FuncModel(
name = name.value, name = name.value,
args = ArgsDef( args = ArgsDef(
argNames argNames
.zip(funcArrow.args) .zip(funcArrow.args)
.map { .map {
case (n, dt: DataType) => ArgDef.Data(n, dt) case (n, dt: DataType) => ArgDef.Data(n, dt)
case (n, at: ArrowType) => ArgDef.Arrow(n, at) case (n, at: ArrowType) => ArgDef.Arrow(n, at)
} }
), ),
ret = retValue ret = (retValue, retType, funcArrow.res).mapN { case (retV, retT, resT) =>
.map(ValuesAlgebra.valueToModel) ValuesAlgebra.valueToModel(retV, retT) -> resT
.flatMap(vd => funcArrow.res.map(Call.Arg(vd, _))), },
body = bg body = bg
) )
N.defineArrow( N.defineArrow(
name, name,
funcArrow, funcArrow,
isRoot = true isRoot = true
) as model ) as model
case m => Free.pure[Alg, Model](Model.error("Function body is not a funcOp, it's " + m)) case m => Free.pure[Alg, Model](Model.error("Function body is not a funcOp, it's " + m))
}) })
)
def program[Alg[_]](implicit def program[Alg[_]](implicit
T: TypesAlgebra[F, Alg], T: TypesAlgebra[F, Alg],

View File

@ -6,6 +6,7 @@ import aqua.parser.expr.IfExpr
import aqua.semantics.rules.ValuesAlgebra import aqua.semantics.rules.ValuesAlgebra
import aqua.semantics.rules.types.TypesAlgebra import aqua.semantics.rules.types.TypesAlgebra
import aqua.semantics.Prog import aqua.semantics.Prog
import aqua.types.Type
import cats.free.Free import cats.free.Free
import cats.syntax.functor._ import cats.syntax.functor._
@ -21,26 +22,30 @@ class IfSem[F[_]](val expr: IfExpr[F]) extends AnyVal {
V.resolveType(expr.right).flatMap { V.resolveType(expr.right).flatMap {
case Some(rt) => case Some(rt) =>
T.ensureTypeMatches(expr.right, lt, rt) T.ensureTypeMatches(expr.right, lt, rt)
.map(m => Some(lt -> rt).filter(_ => m))
case None => case None =>
Free.pure[Alg, Boolean](false) Free.pure[Alg, Option[(Type, Type)]](None)
} }
case None => case None =>
V.resolveType(expr.right).as(false) V.resolveType(expr.right).as[Option[(Type, Type)]](None)
}, },
(r: Boolean, ops: Model) => (r: Option[(Type, Type)], ops: Model) =>
ops match { r.fold(Free.pure[Alg, Model](Model.error("If expression errored in matching types"))) {
case op: FuncOp if r => case (lt, rt) =>
Free.pure[Alg, Model]( ops match {
FuncOp.wrap( case op: FuncOp =>
MatchMismatchTag( Free.pure[Alg, Model](
ValuesAlgebra.valueToModel(expr.left), FuncOp.wrap(
ValuesAlgebra.valueToModel(expr.right), MatchMismatchTag(
expr.eqOp.value ValuesAlgebra.valueToModel(expr.left, lt),
), ValuesAlgebra.valueToModel(expr.right, rt),
op expr.eqOp.value
) ),
) op
case _ => Free.pure[Alg, Model](Model.error("If expression errored")) )
)
case _ => Free.pure[Alg, Model](Model.error("Wrong body of the if expression"))
}
} }
) )
} }

View File

@ -6,6 +6,7 @@ import aqua.parser.expr.OnExpr
import aqua.semantics.Prog import aqua.semantics.Prog
import aqua.semantics.rules.ValuesAlgebra import aqua.semantics.rules.ValuesAlgebra
import aqua.semantics.rules.abilities.AbilitiesAlgebra import aqua.semantics.rules.abilities.AbilitiesAlgebra
import aqua.types.ScalarType
import cats.data.Chain import cats.data.Chain
import cats.syntax.flatMap._ import cats.syntax.flatMap._
import cats.syntax.functor._ import cats.syntax.functor._
@ -28,8 +29,8 @@ class OnSem[F[_]](val expr: OnExpr[F]) extends AnyVal {
case op: FuncOp => case op: FuncOp =>
FuncOp.wrap( FuncOp.wrap(
OnTag( OnTag(
ValuesAlgebra.valueToModel(expr.peerId), ValuesAlgebra.valueToModel(expr.peerId, ScalarType.string),
Chain.fromSeq(expr.via).map(ValuesAlgebra.valueToModel) Chain.fromSeq(expr.via).map(ValuesAlgebra.valueToModel(_, ScalarType.string))
), ),
op op
) )

View File

@ -4,7 +4,7 @@ import aqua.model._
import aqua.parser.lexer._ import aqua.parser.lexer._
import aqua.semantics.rules.names.NamesAlgebra import aqua.semantics.rules.names.NamesAlgebra
import aqua.semantics.rules.types.TypesAlgebra 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.data.Chain
import cats.free.Free import cats.free.Free
import cats.syntax.apply._ import cats.syntax.apply._
@ -82,10 +82,10 @@ object ValuesAlgebra {
case (f: IntoField[F]) :: tail => opsToModel(tail).prepend(IntoFieldModel(f.value)) 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 { v match {
case l: Literal[F] => LiteralModel(l.value) 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 implicit def deriveValuesAlgebra[F[_], Alg[_]](implicit

View File

@ -5,7 +5,7 @@ import aqua.types.{ArrowType, Type}
sealed trait NameOp[F[_], T] 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 ConstantDefined[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[ArrowType]]

View File

@ -7,9 +7,10 @@ import cats.free.Free
class NamesAlgebra[F[_], Alg[_]](implicit V: InjectK[NameOp[F, *], Alg]) { class NamesAlgebra[F[_], Alg[_]](implicit V: InjectK[NameOp[F, *], Alg]) {
def read(name: Name[F]): Free[Alg, Option[Type]] = def read(name: Name[F], mustBeDefined: Boolean = true): Free[Alg, Option[Type]] =
Free.liftInject[Alg](ReadName(name)) Free.liftInject[Alg](ReadName(name, mustBeDefined))
// TODO can be implemented via read?
def constantDefined(name: Name[F]): Free[Alg, Option[Type]] = def constantDefined(name: Name[F]): Free[Alg, Option[Type]] =
Free.liftInject[Alg](ConstantDefined(name)) Free.liftInject[Alg](ConstantDefined(name))

View File

@ -37,11 +37,11 @@ class NamesInterpreter[F[_], X](implicit lens: Lens[X, NamesState[F]], error: Re
.orElseF(readName(rn.name.value)) .orElseF(readName(rn.name.value))
.value .value
.flatTap { .flatTap {
case Some(_) => State.pure(()) case None if rn.mustBeDefined =>
case None =>
getState.flatMap(st => getState.flatMap(st =>
report(rn.name, "Undefined name, available: " + st.allNames.mkString(", ")) report(rn.name, "Undefined name, available: " + st.allNames.mkString(", "))
) )
case _ => State.pure(())
} }
case rn: ConstantDefined[F] => case rn: ConstantDefined[F] =>
constantDefined(rn.name.value) constantDefined(rn.name.value)

View File

@ -99,6 +99,7 @@ class TypesInterpreter[F[_], X](implicit lens: Lens[X, TypesState[F]], error: Re
} }
case etm: EnsureTypeMatches[F] => case etm: EnsureTypeMatches[F] =>
// TODO in case of two literals, check for types intersection?
if (etm.expected.acceptsValueOf(etm.`given`)) State.pure(true) if (etm.expected.acceptsValueOf(etm.`given`)) State.pure(true)
else else
report(etm.token, s"Types mismatch, expected: ${etm.expected}, given: ${etm.`given`}") report(etm.token, s"Types mismatch, expected: ${etm.expected}, given: ${etm.`given`}")

View File

@ -9,10 +9,11 @@ import aqua.parser.lexer.{
IntoField, IntoField,
LambdaOp, LambdaOp,
Name, Name,
StreamTypeToken,
Token, Token,
TypeToken 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.Validated.{Invalid, Valid}
import cats.data.{Chain, NonEmptyChain, ValidatedNec} import cats.data.{Chain, NonEmptyChain, ValidatedNec}
import cats.kernel.Monoid import cats.kernel.Monoid
@ -30,6 +31,10 @@ case class TypesState[F[_]](
resolveTypeToken(dtt).collect { case it: DataType => resolveTypeToken(dtt).collect { case it: DataType =>
ArrayType(it) ArrayType(it)
} }
case StreamTypeToken(_, dtt) =>
resolveTypeToken(dtt).collect { case it: DataType =>
StreamType(it)
}
case ctt: CustomTypeToken[F] => strict.get(ctt.value) case ctt: CustomTypeToken[F] => strict.get(ctt.value)
case btt: BasicTypeToken[F] => Some(btt.value) case btt: BasicTypeToken[F] => Some(btt.value)
case ArrowTypeToken(_, args, res) => case ArrowTypeToken(_, args, res) =>

View File

@ -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(", ")}}" s"$name{${fields.map(_.toString).toNel.toList.map(kv => kv._1 + ": " + kv._2).mkString(", ")}}"
} }
sealed trait CallableType extends Type { case class ArrowType(args: List[Type], res: Option[Type]) 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 CallableType { def acceptsAsArguments(valueTypes: List[Type]): Boolean =
override def acceptsAsArguments(valueTypes: List[Type]): Boolean =
(args.length == valueTypes.length) && args (args.length == valueTypes.length) && args
.zip(valueTypes) .zip(valueTypes)
.forall(av => av._1.acceptsValueOf(av._2)) .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("()") args.map(_.toString).mkString(", ") + " -> " + res.map(_.toString).getOrElse("()")
} }
case class StreamType(element: Type) extends DataType
object Type { object Type {
import Double.NaN 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 == Set(x) => 0.0
case (x: ScalarType, LiteralType(ys, _)) if ys(x) => 1.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: 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)) => case (ProductType(_, xFields), ProductType(_, yFields)) =>
cmpProd(xFields, yFields) cmpProd(xFields, yFields)
case (l: CallableType, r: CallableType) => case (l: ArrowType, r: ArrowType) =>
val argL = l.args val argL = l.args
val resL = l.res val resL = l.res
val argR = r.args val argR = r.args

View File

@ -94,4 +94,13 @@ class TypeSpec extends AnyFlatSpec with Matchers {
accepts(four, one) should be(false) 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)
}
} }