mirror of
https://github.com/fluencelabs/aqua.git
synced 2025-04-24 22:42:13 +00:00
108 option type (#146)
* Option type support: arguments, values * Aqua version 0.1.4 * Fix for optional return value in TS * Tiny fixes * Example for declaring local options
This commit is contained in:
parent
d1e76c1fd3
commit
807c26619f
@ -13,7 +13,13 @@ service Test("test"):
|
||||
getUserList: -> []User
|
||||
doSomething: -> bool
|
||||
|
||||
func betterMessage(relay: string, arr: []string) -> string:
|
||||
func include(opt: ?string) -> string:
|
||||
Peer.is_connected(opt!)
|
||||
for i <- opt:
|
||||
Peer.is_connected(i)
|
||||
<- opt!
|
||||
|
||||
func betterMessage(relay: string, arr: []string, opt: ?string, str: *string) -> ?string:
|
||||
on relay:
|
||||
Peer.is_connected("something")
|
||||
par isOnline <- Peer.is_connected(relay)
|
||||
@ -22,10 +28,16 @@ func betterMessage(relay: string, arr: []string) -> string:
|
||||
|
||||
stream: *string
|
||||
|
||||
localOpt: ?string
|
||||
|
||||
if isOnline:
|
||||
try:
|
||||
Test.doSomething()
|
||||
else:
|
||||
Peer.is_connected(stream!)
|
||||
|
||||
<- stream!2
|
||||
x <- include(arr)
|
||||
y <- include(opt)
|
||||
localOpt <- include(str)
|
||||
|
||||
<- opt
|
@ -3,7 +3,7 @@ package aqua.backend.air
|
||||
import aqua.model._
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.func.body._
|
||||
import aqua.types.StreamType
|
||||
import aqua.types.{OptionType, StreamType}
|
||||
import cats.Eval
|
||||
import cats.data.Chain
|
||||
import cats.free.Cofree
|
||||
@ -30,6 +30,7 @@ object AirGen {
|
||||
case VarModel(name, t, lambda) =>
|
||||
val n = t match {
|
||||
case _: StreamType => "$" + name
|
||||
case _: OptionType => "$" + name
|
||||
case _ => name
|
||||
}
|
||||
if (lambda.isEmpty) DataView.Variable(n)
|
||||
|
@ -18,7 +18,14 @@ case class TypescriptFunc(func: FuncCallable) {
|
||||
|
||||
val tsAir = FuncAirGen(func).generateClientAir(conf)
|
||||
|
||||
val returnCallback = func.ret.as {
|
||||
val returnCallback = func.ret.map(_._2).map {
|
||||
case OptionType(_) =>
|
||||
s"""h.onEvent('${conf.callbackService}', '${conf.respFuncName}', (args) => {
|
||||
| const [res] = args;
|
||||
| resolve(res.length === 0 ? null : res[0]);
|
||||
|});
|
||||
|""".stripMargin
|
||||
case _ =>
|
||||
s"""h.onEvent('${conf.callbackService}', '${conf.respFuncName}', (args) => {
|
||||
| const [res] = args;
|
||||
| resolve(res);
|
||||
@ -28,6 +35,8 @@ case class TypescriptFunc(func: FuncCallable) {
|
||||
}
|
||||
|
||||
val setCallbacks = func.args.args.map {
|
||||
case ArgDef.Data(argName, OptionType(_)) =>
|
||||
s"""h.on('${conf.getDataService}', '$argName', () => {return $argName === null ? [] : [$argName];});"""
|
||||
case ArgDef.Data(argName, _) =>
|
||||
s"""h.on('${conf.getDataService}', '$argName', () => {return $argName;});"""
|
||||
case ArgDef.Arrow(argName, at) =>
|
||||
@ -88,6 +97,7 @@ case class TypescriptFunc(func: FuncCallable) {
|
||||
object TypescriptFunc {
|
||||
|
||||
def typeToTs(t: Type): String = t match {
|
||||
case OptionType(t) => typeToTs(t) + " | null"
|
||||
case ArrayType(t) => typeToTs(t) + "[]"
|
||||
case StreamType(t) => typeToTs(t) + "[]"
|
||||
case pt: ProductType =>
|
||||
|
20
build.sbt
20
build.sbt
@ -1,32 +1,32 @@
|
||||
val dottyVersion = "2.13.5"
|
||||
|
||||
//val dottyVersion = "3.0.0-RC3"
|
||||
//val dottyVersion = "3.0.0"
|
||||
|
||||
scalaVersion := dottyVersion
|
||||
|
||||
val baseAquaVersion = settingKey[String]("base aqua version")
|
||||
|
||||
val catsV = "2.6.0"
|
||||
val catsParseV = "0.3.3"
|
||||
val catsV = "2.6.1"
|
||||
val catsParseV = "0.3.4"
|
||||
val monocleV = "3.0.0-M5"
|
||||
val scalaTestV = "3.2.7" // TODO update version for scala 3-RC3
|
||||
val fs2V = "3.0.2"
|
||||
val catsEffectV = "3.1.0"
|
||||
val scalaTestV = "3.2.9"
|
||||
val fs2V = "3.0.4"
|
||||
val catsEffectV = "3.1.1"
|
||||
val airframeLogV = "21.5.4"
|
||||
val log4catsV = "2.1.1"
|
||||
val enumeratumV = "1.6.1"
|
||||
val slf4jV = "1.7.25"
|
||||
val enumeratumV = "1.6.1" // Scala3 issue: https://github.com/lloydmeta/enumeratum/issues/300
|
||||
val slf4jV = "1.7.30"
|
||||
val declineV = "2.0.0-RC1" // Scala3 issue: https://github.com/bkirwi/decline/issues/260
|
||||
val declineEnumV = "1.3.0"
|
||||
|
||||
name := "aqua-hll"
|
||||
|
||||
val commons = Seq(
|
||||
baseAquaVersion := "0.1.3",
|
||||
baseAquaVersion := "0.1.4",
|
||||
version := baseAquaVersion.value + "-" + sys.env.getOrElse("BUILD_NUMBER", "SNAPSHOT"),
|
||||
scalaVersion := dottyVersion,
|
||||
libraryDependencies ++= Seq(
|
||||
"org.typelevel" %% "log4cats-core" % "2.1.1",
|
||||
"org.typelevel" %% "log4cats-core" % log4catsV,
|
||||
"org.scalatest" %% "scalatest" % scalaTestV % Test
|
||||
),
|
||||
addCompilerPlugin("org.typelevel" %% "kind-projector" % "0.11.3" cross CrossVersion.full)
|
||||
|
@ -10,6 +10,11 @@ object FuncOps {
|
||||
def noop(peerId: ValueModel): FuncOp =
|
||||
FuncOp.leaf(CallServiceTag(LiteralModel.quote("op"), "identity", Call(Nil, None), Some(peerId)))
|
||||
|
||||
def identity(what: ValueModel, to: Call.Export): FuncOp =
|
||||
FuncOp.leaf(
|
||||
CallServiceTag(LiteralModel.quote("op"), "identity", Call(what :: Nil, Some(to)), None)
|
||||
)
|
||||
|
||||
def callService(srvId: ValueModel, funcName: String, call: Call): FuncOp =
|
||||
FuncOp.leaf(
|
||||
CallServiceTag(
|
||||
@ -48,4 +53,13 @@ object FuncOps {
|
||||
|
||||
def xor(left: FuncOp, right: FuncOp): FuncOp =
|
||||
FuncOp.node(XorTag, Chain(left, right))
|
||||
|
||||
def fold(item: String, iter: ValueModel, op: FuncOp): FuncOp =
|
||||
FuncOp.wrap(
|
||||
ForTag(item, iter),
|
||||
op
|
||||
)
|
||||
|
||||
def next(item: String): FuncOp =
|
||||
FuncOp.leaf(NextTag(item))
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
package aqua.model.transform
|
||||
|
||||
import aqua.model.ValueModel
|
||||
import aqua.model.{ValueModel, VarModel}
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.func.body.{FuncOp, FuncOps}
|
||||
import aqua.types.DataType
|
||||
import aqua.types.{ArrayType, DataType, OptionType, StreamType, Type}
|
||||
import cats.data.Chain
|
||||
|
||||
trait ArgsProvider {
|
||||
def transform(op: FuncOp): FuncOp
|
||||
@ -12,12 +13,39 @@ trait ArgsProvider {
|
||||
case class ArgsFromService(dataServiceId: ValueModel, names: List[(String, DataType)])
|
||||
extends ArgsProvider {
|
||||
|
||||
private def getDataElOp(name: String, t: DataType, el: Type): FuncOp = {
|
||||
val iter = s"$name-iter"
|
||||
val item = s"$name-item"
|
||||
FuncOps.seq(
|
||||
FuncOps.callService(
|
||||
dataServiceId,
|
||||
name,
|
||||
Call(Nil, Some(Call.Export(iter, t)))
|
||||
),
|
||||
FuncOps.fold(
|
||||
item,
|
||||
VarModel(iter, ArrayType(el), Chain.empty),
|
||||
FuncOps.seq(
|
||||
FuncOps.identity(VarModel(item, el), Call.Export(name, t)),
|
||||
FuncOps.next(item)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
def getDataOp(name: String, t: DataType): FuncOp =
|
||||
t match {
|
||||
case StreamType(el) =>
|
||||
getDataElOp(name, t, el)
|
||||
case OptionType(el) =>
|
||||
getDataElOp(name, StreamType(el), el)
|
||||
case _ =>
|
||||
FuncOps.callService(
|
||||
dataServiceId,
|
||||
name,
|
||||
Call(Nil, Some(Call.Export(name, t)))
|
||||
)
|
||||
}
|
||||
|
||||
def transform(op: FuncOp): FuncOp =
|
||||
FuncOps.seq(
|
||||
|
@ -29,6 +29,18 @@ object StreamTypeToken {
|
||||
|
||||
}
|
||||
|
||||
case class OptionTypeToken[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 OptionTypeToken {
|
||||
|
||||
def `optiontypedef`[F[_]: LiftParser: Comonad]: P[OptionTypeToken[F]] =
|
||||
(`?`.lift ~ DataTypeToken.`datatypedef`[F]).map(ud => OptionTypeToken(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)
|
||||
|
||||
@ -94,7 +106,9 @@ object DataTypeToken {
|
||||
|
||||
def `datatypedef`[F[_]: LiftParser: Comonad]: P[DataTypeToken[F]] =
|
||||
P.oneOf(
|
||||
P.defer(`arraytypedef`[F]) :: P.defer(StreamTypeToken.`streamtypedef`) :: BasicTypeToken
|
||||
P.defer(`arraytypedef`[F]) :: P.defer(StreamTypeToken.`streamtypedef`) :: P.defer(
|
||||
OptionTypeToken.`optiontypedef`
|
||||
) :: BasicTypeToken
|
||||
.`basictypedef`[F] :: CustomTypeToken.ct[F] :: Nil
|
||||
)
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ 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 aqua.types.{ArrayType, OptionType, StreamType}
|
||||
import cats.free.Free
|
||||
|
||||
class DeclareStreamSem[F[_]](val expr: DeclareStreamExpr[F]) {
|
||||
@ -19,6 +19,8 @@ class DeclareStreamSem[F[_]](val expr: DeclareStreamExpr[F]) {
|
||||
.flatMap {
|
||||
case Some(t: StreamType) =>
|
||||
N.define(expr.name, t)
|
||||
case Some(t: OptionType) =>
|
||||
N.define(expr.name, StreamType(t.element))
|
||||
case Some(at @ ArrayType(t)) =>
|
||||
T.ensureTypeMatches(expr.`type`, StreamType(t), at)
|
||||
case Some(t) =>
|
||||
|
@ -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, StreamType, Type}
|
||||
import aqua.types.{ArrayType, BoxType}
|
||||
import cats.data.Chain
|
||||
import cats.free.Free
|
||||
import cats.syntax.flatMap._
|
||||
@ -24,10 +24,8 @@ class ForSem[F[_]](val expr: ForExpr[F]) extends AnyVal {
|
||||
N.beginScope(expr.item) >> V.valueToModel(expr.iterable).flatMap[Option[ValueModel]] {
|
||||
case Some(vm) =>
|
||||
vm.lastType match {
|
||||
case at @ ArrayType(t) =>
|
||||
N.define(expr.item, t).as(Option(vm))
|
||||
case st @ StreamType(t) =>
|
||||
N.define(expr.item, t).as(Option(vm))
|
||||
case t: BoxType =>
|
||||
N.define(expr.item, t.element).as(Option(vm))
|
||||
case dt =>
|
||||
T.ensureTypeMatches(expr.iterable, ArrayType(dt), dt).as(Option.empty[ValueModel])
|
||||
}
|
||||
|
@ -11,11 +11,12 @@ import aqua.parser.lexer.{
|
||||
IntoIndex,
|
||||
LambdaOp,
|
||||
Name,
|
||||
OptionTypeToken,
|
||||
StreamTypeToken,
|
||||
Token,
|
||||
TypeToken
|
||||
}
|
||||
import aqua.types.{ArrayType, ArrowType, DataType, ProductType, StreamType, Type}
|
||||
import aqua.types.{ArrayType, ArrowType, DataType, OptionType, ProductType, StreamType, Type}
|
||||
import cats.data.Validated.{Invalid, Valid}
|
||||
import cats.data.{Chain, NonEmptyChain, ValidatedNec}
|
||||
import cats.kernel.Monoid
|
||||
@ -37,6 +38,10 @@ case class TypesState[F[_]](
|
||||
resolveTypeToken(dtt).collect { case it: DataType =>
|
||||
StreamType(it)
|
||||
}
|
||||
case OptionTypeToken(_, dtt) =>
|
||||
resolveTypeToken(dtt).collect { case it: DataType =>
|
||||
OptionType(it)
|
||||
}
|
||||
case ctt: CustomTypeToken[F] => strict.get(ctt.value)
|
||||
case btt: BasicTypeToken[F] => Some(btt.value)
|
||||
case ArrowTypeToken(_, args, res) =>
|
||||
@ -99,7 +104,15 @@ case class TypesState[F[_]](
|
||||
resolveOps(intern, tail).map(IntoIndexModel(i.value, intern) :: _)
|
||||
case StreamType(intern) =>
|
||||
resolveOps(intern, tail).map(IntoIndexModel(i.value, intern) :: _)
|
||||
case _ => Left(i -> s"Expected $rootT to be an array")
|
||||
case OptionType(intern) =>
|
||||
i.value match {
|
||||
case 0 =>
|
||||
resolveOps(intern, tail).map(IntoIndexModel(i.value, intern) :: _)
|
||||
case _ =>
|
||||
Left(i -> s"Option might have only one element, use ! to get it")
|
||||
}
|
||||
|
||||
case _ => Left(i -> s"Expected $rootT to be an array or a stream")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,10 +83,18 @@ object LiteralType {
|
||||
val string = LiteralType(Set(ScalarType.string), "string")
|
||||
}
|
||||
|
||||
case class ArrayType(element: Type) extends DataType {
|
||||
sealed trait BoxType extends DataType {
|
||||
def element: Type
|
||||
}
|
||||
|
||||
case class ArrayType(element: Type) extends BoxType {
|
||||
override def toString: String = "[]" + element
|
||||
}
|
||||
|
||||
case class OptionType(element: Type) extends BoxType {
|
||||
override def toString: String = "?" + element
|
||||
}
|
||||
|
||||
case class ProductType(name: String, fields: NonEmptyMap[String, Type]) extends DataType {
|
||||
|
||||
override def toString: String =
|
||||
@ -104,7 +112,7 @@ case class ArrowType(args: List[Type], res: Option[Type]) extends Type {
|
||||
args.map(_.toString).mkString(", ") + " -> " + res.map(_.toString).getOrElse("()")
|
||||
}
|
||||
|
||||
case class StreamType(element: Type) extends DataType
|
||||
case class StreamType(element: Type) extends BoxType
|
||||
|
||||
object Type {
|
||||
import Double.NaN
|
||||
@ -149,6 +157,9 @@ object Type {
|
||||
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: ArrayType, y: OptionType) => cmp(x.element, y.element)
|
||||
case (x: OptionType, y: StreamType) => cmp(x.element, y.element)
|
||||
case (x: OptionType, y: ArrayType) => cmp(x.element, y.element)
|
||||
case (x: StreamType, y: StreamType) => cmp(x.element, y.element)
|
||||
case (ProductType(_, xFields), ProductType(_, yFields)) =>
|
||||
cmpProd(xFields, yFields)
|
||||
|
@ -12,6 +12,8 @@ class TypeSpec extends AnyFlatSpec with Matchers {
|
||||
import aqua.types.ScalarType._
|
||||
|
||||
def `[]`(t: DataType): DataType = ArrayType(t)
|
||||
def `?`(t: DataType): DataType = OptionType(t)
|
||||
def `*`(t: DataType): DataType = StreamType(t)
|
||||
|
||||
def accepts(recv: Type, incoming: Type) =
|
||||
recv >= incoming
|
||||
@ -103,4 +105,13 @@ class TypeSpec extends AnyFlatSpec with Matchers {
|
||||
accepts(stream, stream) should be(true)
|
||||
}
|
||||
|
||||
"streams" should "be accepted as an option, but not vice versa" in {
|
||||
val stream: Type = StreamType(bool)
|
||||
val opt: Type = OptionType(bool)
|
||||
|
||||
accepts(opt, stream) should be(true)
|
||||
accepts(stream, opt) should be(false)
|
||||
accepts(opt, opt) should be(true)
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user