mirror of
https://github.com/fluencelabs/aqua.git
synced 2024-12-04 14:40:17 +00:00
Return many values from a single function (#229)
This commit is contained in:
parent
cd30ff8e8c
commit
3eb3ecc221
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -49,7 +49,7 @@ jobs:
|
||||
|
||||
- name: Check .jar exists
|
||||
run: |
|
||||
JAR="cli/target/scala-2.13/aqua-cli-${{ env.VERSION }}.jar"
|
||||
JAR="cli/target/scala-3.0.1/aqua-cli-${{ env.VERSION }}.jar"
|
||||
stat "$JAR"
|
||||
echo "JAR=$JAR" >> $GITHUB_ENV
|
||||
|
||||
|
@ -1,16 +1,15 @@
|
||||
data DT:
|
||||
field: string
|
||||
service Getter("test"):
|
||||
createStr: u32 -> string
|
||||
|
||||
service DTGetter("get-dt"):
|
||||
get_dt(s: string) -> DT
|
||||
service OpO("op"):
|
||||
identity: string -> string
|
||||
|
||||
func use_name1(name: string) -> string:
|
||||
results <- DTGetter.get_dt(name)
|
||||
<- results.field
|
||||
-- a question mark means that this constant could be rewritten before this definition
|
||||
const anotherConst ?= "default-str"
|
||||
const uniqueConst ?= 5
|
||||
|
||||
func use_name2(name: string) -> []string:
|
||||
results: *string
|
||||
results <- use_name1(name)
|
||||
results <- use_name1(name)
|
||||
results <- use_name1(name)
|
||||
<- results
|
||||
func callConstant() -> []string, u8:
|
||||
res: *string
|
||||
res <- Getter.createStr(uniqueConst)
|
||||
res <- OpO.identity(anotherConst)
|
||||
<- res, 5
|
@ -40,7 +40,7 @@ object AirGen extends LogSupport {
|
||||
def opsToSingle(ops: Chain[AirGen]): AirGen = ops.toList match {
|
||||
case Nil => NullGen
|
||||
case h :: Nil => h
|
||||
case list => list.reduceLeft(SeqGen)
|
||||
case list => list.reduceLeft(SeqGen(_, _))
|
||||
}
|
||||
|
||||
private def folder(op: ResolvedOp, ops: Chain[AirGen]): Eval[AirGen] =
|
||||
@ -48,12 +48,12 @@ object AirGen extends LogSupport {
|
||||
// case mt: MetaTag =>
|
||||
// folder(mt.op, ops).map(ag => mt.comment.fold(ag)(CommentGen(_, ag)))
|
||||
case SeqRes =>
|
||||
Eval later ops.toList.reduceLeftOption(SeqGen).getOrElse(NullGen)
|
||||
Eval later ops.toList.reduceLeftOption(SeqGen(_, _)).getOrElse(NullGen)
|
||||
case ParRes =>
|
||||
Eval later (ops.toList match {
|
||||
case o :: Nil => ParGen(o, NullGen)
|
||||
case _ =>
|
||||
ops.toList.reduceLeftOption(ParGen).getOrElse {
|
||||
ops.toList.reduceLeftOption(ParGen(_, _)).getOrElse {
|
||||
warn("ParRes with no children converted to Null")
|
||||
NullGen
|
||||
}
|
||||
@ -62,7 +62,7 @@ object AirGen extends LogSupport {
|
||||
Eval later (ops.toList match {
|
||||
case o :: Nil => XorGen(o, NullGen)
|
||||
case _ =>
|
||||
ops.toList.reduceLeftOption(XorGen).getOrElse {
|
||||
ops.toList.reduceLeftOption(XorGen(_, _)).getOrElse {
|
||||
warn("XorRes with no children converted to Null")
|
||||
NullGen
|
||||
}
|
||||
|
@ -27,6 +27,18 @@ case class JavaScriptFunc(func: FuncCallable) {
|
||||
| opt = opt[0];
|
||||
| }
|
||||
| return resolve(opt);""".stripMargin
|
||||
case pt: ProductType =>
|
||||
val unwrapOpts = pt.toList.zipWithIndex.collect { case (OptionType(_), i) =>
|
||||
s"""
|
||||
| if(Array.isArray(opt[$i])) {
|
||||
| if (opt[$i].length === 0) { opt[$i] = null; }
|
||||
| else {opt[$i] = opt[$i][0]; }
|
||||
| }""".stripMargin
|
||||
}.mkString
|
||||
|
||||
s""" let opt = args;
|
||||
|$unwrapOpts
|
||||
| return resolve(opt);""".stripMargin
|
||||
case _ =>
|
||||
""" const [res] = args;
|
||||
| resolve(res);""".stripMargin
|
||||
@ -51,18 +63,15 @@ case class JavaScriptFunc(func: FuncCallable) {
|
||||
val value = s"$argName(${argsCallToJs(
|
||||
at
|
||||
)})"
|
||||
val expr = at.res.fold(s"$value; return {}")(_ => s"return $value")
|
||||
val expr = at.codomain.uncons.fold(s"$value; return {}")(_ => s"return $value")
|
||||
s"""h.on('${conf.callbackService}', '$argName', (args) => {$expr;});"""
|
||||
}
|
||||
.mkString("\n")
|
||||
|
||||
// TODO support multi-return
|
||||
val returnCallback = func.arrowType.codomain.uncons
|
||||
.map(_._1)
|
||||
.map(t => genReturnCallback(t, conf.callbackService, conf.respFuncName))
|
||||
.map(_ => genReturnCallback(func.arrowType.codomain, conf.callbackService, conf.respFuncName))
|
||||
.getOrElse("")
|
||||
|
||||
// TODO support multi-return
|
||||
val returnVal =
|
||||
func.ret.headOption.fold("Promise.race([promise, Promise.resolve()])")(_ => "promise")
|
||||
|
||||
|
@ -35,6 +35,18 @@ case class TypeScriptFunc(func: FuncCallable) {
|
||||
| opt = opt[0];
|
||||
| }
|
||||
| return resolve(opt);""".stripMargin
|
||||
case pt: ProductType =>
|
||||
val unwrapOpts = pt.toList.zipWithIndex.collect { case (OptionType(_), i) =>
|
||||
s"""
|
||||
| if(Array.isArray(opt[$i])) {
|
||||
| if (opt[$i].length === 0) { opt[$i] = null; }
|
||||
| else {opt[$i] = opt[$i][0]; }
|
||||
| }""".stripMargin
|
||||
}.mkString
|
||||
|
||||
s""" let opt: any = args;
|
||||
|$unwrapOpts
|
||||
| return resolve(opt);""".stripMargin
|
||||
case _ =>
|
||||
""" const [res] = args;
|
||||
| resolve(res);""".stripMargin
|
||||
@ -51,8 +63,9 @@ case class TypeScriptFunc(func: FuncCallable) {
|
||||
val tsAir = FuncAirGen(func).generateAir(conf)
|
||||
|
||||
// TODO: support multi return
|
||||
val retType = func.arrowType.codomain.uncons
|
||||
.map(_._1)
|
||||
val retType =
|
||||
if (func.arrowType.codomain.length > 1) Some(func.arrowType.codomain)
|
||||
else func.arrowType.codomain.uncons.map(_._1)
|
||||
val retTypeTs = retType
|
||||
.fold("void")(typeToTs)
|
||||
|
||||
@ -133,8 +146,10 @@ object TypeScriptFunc {
|
||||
case OptionType(t) => typeToTs(t) + " | null"
|
||||
case ArrayType(t) => typeToTs(t) + "[]"
|
||||
case StreamType(t) => typeToTs(t) + "[]"
|
||||
case pt: StructType =>
|
||||
s"{${pt.fields.map(typeToTs).toNel.map(kv => kv._1 + ":" + kv._2).toList.mkString(";")}}"
|
||||
case pt: ProductType =>
|
||||
"[" + pt.toList.map(typeToTs).mkString(", ") + "]"
|
||||
case st: StructType =>
|
||||
s"{${st.fields.map(typeToTs).toNel.map(kv => kv._1 + ":" + kv._2).toList.mkString(";")}}"
|
||||
case st: ScalarType if ScalarType.number(st) => "number"
|
||||
case ScalarType.bool => "boolean"
|
||||
case ScalarType.string => "string"
|
||||
@ -157,6 +172,6 @@ object TypeScriptFunc {
|
||||
.mkString(", ")
|
||||
|
||||
def argsCallToTs(at: ArrowType): String =
|
||||
at.args.zipWithIndex.map(_._2).map(idx => s"args[$idx]").mkString(", ")
|
||||
at.domain.toList.zipWithIndex.map(_._2).map(idx => s"args[$idx]").mkString(", ")
|
||||
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ val cats = "org.typelevel" %% "cats-core" % catsV
|
||||
name := "aqua-hll"
|
||||
|
||||
val commons = Seq(
|
||||
baseAquaVersion := "0.1.12",
|
||||
baseAquaVersion := "0.1.13",
|
||||
version := baseAquaVersion.value + "-" + sys.env.getOrElse("BUILD_NUMBER", "SNAPSHOT"),
|
||||
scalaVersion := dottyVersion,
|
||||
libraryDependencies ++= Seq(
|
||||
|
@ -1,3 +1,3 @@
|
||||
package aqua.model
|
||||
|
||||
object ReturnModel extends Model {}
|
||||
object ReturnModel extends Model
|
||||
|
@ -16,11 +16,11 @@ case class ResolveFunc(
|
||||
|
||||
private val returnVar: String = "-return-"
|
||||
|
||||
def returnCallback(retModel: ValueModel): FuncOp =
|
||||
def returnCallback(retModel: List[ValueModel]): FuncOp =
|
||||
callback(
|
||||
respFuncName,
|
||||
Call(
|
||||
retModel :: Nil,
|
||||
retModel,
|
||||
Nil
|
||||
)
|
||||
)
|
||||
@ -55,9 +55,9 @@ case class ResolveFunc(
|
||||
func.arrowType.domain.toLabelledList().map(ad => VarModel(ad._1, ad._2)),
|
||||
returnType.map { case (l, t) => Call.Export(l, t) }
|
||||
)
|
||||
) ::
|
||||
returnType.map { case (l, t) => VarModel(l, t) }
|
||||
.map(returnCallback): _*
|
||||
) :: returnType.headOption
|
||||
.map(_ => returnCallback(returnType.map { case (l, t) => VarModel(l, t) }))
|
||||
.toList: _*
|
||||
)
|
||||
),
|
||||
ArrowType(ConsType.cons(func.funcName, func.arrowType, NilType), NilType),
|
||||
|
@ -1,8 +1,8 @@
|
||||
package aqua.parser.expr
|
||||
|
||||
import aqua.parser.Expr
|
||||
import aqua.parser.lexer.Token._
|
||||
import aqua.parser.lexer.{ArrowTypeToken, Name}
|
||||
import aqua.parser.lexer.Token.*
|
||||
import aqua.parser.lexer.{ArrowTypeToken, DataTypeToken, Name}
|
||||
import aqua.parser.lift.LiftParser
|
||||
import cats.Comonad
|
||||
import cats.parse.Parser
|
||||
@ -14,8 +14,9 @@ object ArrowTypeExpr extends Expr.Leaf {
|
||||
|
||||
override def p[F[_]: LiftParser: Comonad]: Parser[ArrowTypeExpr[F]] =
|
||||
(Name
|
||||
.p[F] ~ ((` : ` *> ArrowTypeToken.`arrowdef`[F]) | ArrowTypeToken.`arrowWithNames`)).map {
|
||||
case (name, t) =>
|
||||
ArrowTypeExpr(name, t)
|
||||
.p[F] ~ ((` : ` *> ArrowTypeToken.`arrowdef`[F](
|
||||
DataTypeToken.`datatypedef`[F]
|
||||
)) | ArrowTypeToken.`arrowWithNames`(DataTypeToken.`datatypedef`[F]))).map { case (name, t) =>
|
||||
ArrowTypeExpr(name, t)
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import cats.Comonad
|
||||
import cats.parse.{Parser => P}
|
||||
|
||||
case class CallArrowExpr[F[_]](
|
||||
variable: Option[Name[F]],
|
||||
variables: List[Name[F]],
|
||||
ability: Option[Ability[F]],
|
||||
funcName: Name[F],
|
||||
args: List[Value[F]]
|
||||
@ -17,12 +17,12 @@ case class CallArrowExpr[F[_]](
|
||||
object CallArrowExpr extends Expr.Leaf {
|
||||
|
||||
override def p[F[_]: LiftParser: Comonad]: P[CallArrowExpr[F]] =
|
||||
((Name.p[F] <* ` <- `).backtrack.?.with1 ~
|
||||
((comma(Name.p[F]) <* ` <- `).backtrack.?.with1 ~
|
||||
((Ability.ab[F] <* `.`).?.with1 ~
|
||||
Name.p[F] ~
|
||||
comma0(Value.`value`[F].surroundedBy(`/s*`)).between(`(` <* `/s*`, `/s*` *> `)`))).map {
|
||||
case (variable, ((ability, funcName), args)) =>
|
||||
CallArrowExpr(variable, ability, funcName, args)
|
||||
case (variables, ((ability, funcName), args)) =>
|
||||
CallArrowExpr(variables.toList.flatMap(_.toList), ability, funcName, args)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package aqua.parser.expr
|
||||
|
||||
import aqua.parser.lexer.Token._
|
||||
import aqua.parser.lexer.{Arg, DataTypeToken, Name, Value}
|
||||
import aqua.parser.lexer.Token.*
|
||||
import aqua.parser.lexer.{Arg, ArrowTypeToken, DataTypeToken, Name, TypeToken, Value}
|
||||
import aqua.parser.lift.LiftParser
|
||||
import aqua.parser.{Ast, Expr, FuncReturnError, ParserError}
|
||||
import cats.Comonad
|
||||
@ -11,10 +11,11 @@ import cats.parse.Parser
|
||||
|
||||
case class FuncExpr[F[_]](
|
||||
name: Name[F],
|
||||
args: List[Arg[F]],
|
||||
ret: Option[DataTypeToken[F]],
|
||||
retValue: Option[Value[F]]
|
||||
) extends Expr[F](FuncExpr, name)
|
||||
arrowTypeExpr: ArrowTypeToken[F],
|
||||
retValue: List[Value[F]]
|
||||
) extends Expr[F](FuncExpr, name) {
|
||||
def ret = arrowTypeExpr.res
|
||||
}
|
||||
|
||||
object FuncExpr extends Expr.AndIndented {
|
||||
|
||||
@ -36,10 +37,10 @@ object FuncExpr extends Expr.AndIndented {
|
||||
Nil
|
||||
|
||||
override def p[F[_]: LiftParser: Comonad]: Parser[FuncExpr[F]] =
|
||||
((`func` *> ` ` *> Name.p[F])
|
||||
~ comma0(Arg.p.surroundedBy(`/s*`)).between(`(` <* `/s*`, `/s*` *> `)`)
|
||||
~ (` -> ` *> DataTypeToken.`datatypedef`).?).map { case ((name, args), ret) =>
|
||||
FuncExpr(name, args, ret, None)
|
||||
((`func` *> ` ` *> Name.p[F]) ~ ArrowTypeToken.`arrowWithNames`[F](
|
||||
TypeToken.`typedef`[F]
|
||||
)).map { case (name, arrow) =>
|
||||
FuncExpr(name, arrow.copy(unit = name.unit), Nil)
|
||||
}
|
||||
|
||||
override def ast[F[_]: LiftParser: Comonad](): Parser[ValidatedNec[ParserError[F], Ast.Tree[F]]] =
|
||||
@ -49,34 +50,65 @@ object FuncExpr extends Expr.AndIndented {
|
||||
_.andThen(tree =>
|
||||
tree.head match {
|
||||
case funcExpr: FuncExpr[F] =>
|
||||
// Find the return expression which might be the last one in the function body
|
||||
val maybeReturn =
|
||||
tree.tail.value.lastOption.map(_.head).collect { case re: ReturnExpr[F] =>
|
||||
re
|
||||
}
|
||||
// Find correspondance between returned values and declared return types
|
||||
funcExpr.ret match {
|
||||
case Some(ret) =>
|
||||
tree.tail.value.lastOption.map(_.head) match {
|
||||
case Some(re: ReturnExpr[F]) =>
|
||||
Validated
|
||||
.validNec(Cofree(funcExpr.copy(retValue = Some(re.value)), tree.tail))
|
||||
|
||||
case _ =>
|
||||
Validated.invalidNec(
|
||||
FuncReturnError[F](
|
||||
ret.unit,
|
||||
"Return type is defined for function, but nothing returned. Use `<- value` as the last expression inside function body."
|
||||
)
|
||||
case Nil =>
|
||||
// Nothing should be returned
|
||||
maybeReturn.fold(Validated.validNec(tree))(re =>
|
||||
// Declared nothing, but smth is returned
|
||||
Validated.invalidNec(
|
||||
FuncReturnError[F](
|
||||
re.token.unit,
|
||||
"Trying to return a value from function that has no return type. Please add return type to function declaration, e.g. `func foo() -> RetType:`"
|
||||
)
|
||||
)
|
||||
)
|
||||
case rets =>
|
||||
// Something is expected to be returned
|
||||
maybeReturn.fold(
|
||||
// No values are returned at all, no return expression
|
||||
Validated.invalidNec(
|
||||
FuncReturnError[F](
|
||||
rets.head.unit,
|
||||
"Return type is defined for function, but nothing returned. Use `<- value` as the last expression inside function body."
|
||||
)
|
||||
)
|
||||
) { re =>
|
||||
// Something is returned, so check that numbers are the same
|
||||
def checkRet(typeDef: List[DataTypeToken[F]], values: List[Value[F]])
|
||||
: ValidatedNec[ParserError[F], Ast.Tree[F]] =
|
||||
(typeDef, values) match {
|
||||
case (Nil, Nil) =>
|
||||
// Everything checked, ok
|
||||
Validated
|
||||
.validNec(Cofree(funcExpr.copy(retValue = re.values.toList), tree.tail))
|
||||
case (_ :: tTail, _ :: vTail) =>
|
||||
// One more element checked, advance
|
||||
checkRet(tTail, vTail)
|
||||
case (t :: _, Nil) =>
|
||||
// No more values, but still have declared types
|
||||
Validated.invalidNec(
|
||||
FuncReturnError[F](
|
||||
t.unit,
|
||||
"Return type is defined for function, but nothing returned. Use `<- value, ...` as the last expression inside function body."
|
||||
)
|
||||
)
|
||||
case (_, v :: _) =>
|
||||
Validated.invalidNec(
|
||||
FuncReturnError[F](
|
||||
v.unit,
|
||||
"Return type is not defined for function, but something is returned."
|
||||
)
|
||||
)
|
||||
}
|
||||
checkRet(rets, re.values.toList)
|
||||
}
|
||||
|
||||
case None =>
|
||||
tree.tail.value.lastOption.map(_.head) match {
|
||||
case Some(re: ReturnExpr[F]) =>
|
||||
Validated.invalidNec(
|
||||
FuncReturnError[F](
|
||||
re.value.unit,
|
||||
"Trying to return a value from function that has no return type. Please add return type to function declaration, e.g. `func foo() -> RetType:`"
|
||||
)
|
||||
)
|
||||
case _ =>
|
||||
Validated.validNec(tree)
|
||||
}
|
||||
}
|
||||
|
||||
case _ => Validated.validNec(tree)
|
||||
|
@ -1,16 +1,17 @@
|
||||
package aqua.parser.expr
|
||||
|
||||
import aqua.parser.Expr
|
||||
import aqua.parser.lexer.Token._
|
||||
import aqua.parser.lexer.Token.*
|
||||
import aqua.parser.lexer.Value
|
||||
import aqua.parser.lift.LiftParser
|
||||
import cats.Comonad
|
||||
import cats.data.NonEmptyList
|
||||
import cats.parse.Parser
|
||||
|
||||
case class ReturnExpr[F[_]](value: Value[F]) extends Expr[F](ReturnExpr, value)
|
||||
case class ReturnExpr[F[_]](values: NonEmptyList[Value[F]]) extends Expr[F](ReturnExpr, values.head)
|
||||
|
||||
object ReturnExpr extends Expr.Leaf {
|
||||
|
||||
override def p[F[_]: LiftParser: Comonad]: Parser[ReturnExpr[F]] =
|
||||
(`<-` *> ` ` *> Value.`value`[F]).map(ReturnExpr(_))
|
||||
(`<-` *> ` ` *> comma(Value.`value`[F])).map(ReturnExpr(_))
|
||||
}
|
||||
|
@ -72,38 +72,32 @@ object BasicTypeToken {
|
||||
.map(BasicTypeToken(_))
|
||||
}
|
||||
|
||||
sealed trait ArrowDef[F[_]] {
|
||||
def argTypes: List[TypeToken[F]]
|
||||
def resType: Option[DataTypeToken[F]]
|
||||
}
|
||||
|
||||
case class ArrowTypeToken[F[_]: Comonad](
|
||||
override val unit: F[Unit],
|
||||
args: List[DataTypeToken[F]],
|
||||
res: Option[DataTypeToken[F]]
|
||||
) extends TypeToken[F] with ArrowDef[F] {
|
||||
args: List[(Option[Name[F]], TypeToken[F])],
|
||||
res: List[DataTypeToken[F]]
|
||||
) extends TypeToken[F] {
|
||||
override def as[T](v: T): F[T] = unit.as(v)
|
||||
|
||||
override def argTypes: List[TypeToken[F]] = args
|
||||
|
||||
override def resType: Option[DataTypeToken[F]] = res
|
||||
def argTypes: List[TypeToken[F]] = args.map(_._2)
|
||||
}
|
||||
|
||||
object ArrowTypeToken {
|
||||
|
||||
def `arrowdef`[F[_]: LiftParser: Comonad]: P[ArrowTypeToken[F]] =
|
||||
(comma0(DataTypeToken.`datatypedef`).with1 ~ ` -> `.lift ~
|
||||
(DataTypeToken.`datatypedef`
|
||||
.map(Some(_)) | `()`.as(None))).map { case ((args, point), res) ⇒
|
||||
ArrowTypeToken(point, args, res)
|
||||
def `arrowdef`[F[_]: LiftParser: Comonad](argTypeP: P[TypeToken[F]]): P[ArrowTypeToken[F]] =
|
||||
(comma0(argTypeP).with1 ~ ` -> `.lift ~
|
||||
(comma(DataTypeToken.`datatypedef`).map(_.toList)
|
||||
| `()`.as(Nil))).map { case ((args, point), res) ⇒
|
||||
ArrowTypeToken(point, args.map(Option.empty[Name[F]] -> _), res)
|
||||
}
|
||||
|
||||
def `arrowWithNames`[F[_]: LiftParser: Comonad]: P[ArrowTypeToken[F]] =
|
||||
def `arrowWithNames`[F[_]: LiftParser: Comonad](argTypeP: P[TypeToken[F]]): P[ArrowTypeToken[F]] =
|
||||
(((`(`.lift <* `/s*`) ~ comma0(
|
||||
(Name.p[F] *> ` : ` *> DataTypeToken.`datatypedef`).surroundedBy(`/s*`)
|
||||
(Name.p[F].map(Option(_)) ~ (` : ` *> (argTypeP | argTypeP.between(`(`, `)`))))
|
||||
.surroundedBy(`/s*`)
|
||||
) <* (`/s*` *> `)`)) ~
|
||||
(` -> ` *> DataTypeToken.`datatypedef`).?).map { case ((point, args), res) =>
|
||||
ArrowTypeToken(point, args, res)
|
||||
(` -> ` *> comma(DataTypeToken.`datatypedef`)).?).map { case ((point, args), res) =>
|
||||
ArrowTypeToken(point, args, res.toList.flatMap(_.toList))
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,7 +124,9 @@ object TypeToken {
|
||||
|
||||
def `typedef`[F[_]: LiftParser: Comonad]: P[TypeToken[F]] =
|
||||
P.oneOf(
|
||||
ArrowTypeToken.`arrowdef`.backtrack :: DataTypeToken.`datatypedef` :: Nil
|
||||
ArrowTypeToken
|
||||
.`arrowdef`((DataTypeToken.`datatypedef`[F]))
|
||||
.backtrack :: DataTypeToken.`datatypedef` :: Nil
|
||||
)
|
||||
|
||||
}
|
||||
|
@ -39,7 +39,13 @@ object AquaSpec {
|
||||
args: List[DataTypeToken[Id]],
|
||||
res: Option[DataTypeToken[Id]]
|
||||
): ArrowTypeToken[Id] =
|
||||
ArrowTypeToken[Id]((), args, res)
|
||||
ArrowTypeToken[Id]((), args.map(None -> _), res.toList)
|
||||
|
||||
def toNamedArrow(
|
||||
args: List[(String, TypeToken[Id])],
|
||||
res: List[DataTypeToken[Id]]
|
||||
): ArrowTypeToken[Id] =
|
||||
ArrowTypeToken[Id]((), args.map(ab => Some(Name[Id](ab._1)) -> ab._2), res)
|
||||
|
||||
implicit def toCustomArg(str: String, customType: String): Arg[Id] =
|
||||
Arg[Id](str, toCustomType(customType))
|
||||
|
@ -2,6 +2,7 @@ package aqua.parser
|
||||
|
||||
import aqua.AquaSpec
|
||||
import aqua.parser.expr.ArrowTypeExpr
|
||||
import aqua.parser.lexer.ArrowTypeToken
|
||||
import aqua.types.ScalarType.{string, u32}
|
||||
import cats.Id
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
@ -21,7 +22,10 @@ class ArrowTypeExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
)
|
||||
|
||||
parseArrow("onIn(a: Custom, b: Custom2)") should be(
|
||||
ArrowTypeExpr[Id]("onIn", toArrowType(List("Custom", "Custom2"), None))
|
||||
ArrowTypeExpr[Id](
|
||||
"onIn",
|
||||
toNamedArrow(List("a" -> toCustomType("Custom"), "b" -> toCustomType("Custom2")), Nil)
|
||||
)
|
||||
)
|
||||
|
||||
parseArrow("onIn: Custom, string, u32, Custom3 -> Custom2") should be(
|
||||
@ -30,5 +34,16 @@ class ArrowTypeExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
toArrowType(List("Custom", string, u32, "Custom3"), Some("Custom2"))
|
||||
)
|
||||
)
|
||||
parseArrow("onIn: Custom, string, u32, Custom3 -> Custom2, string") should be(
|
||||
ArrowTypeExpr[Id](
|
||||
"onIn",
|
||||
ArrowTypeToken[Id](
|
||||
(),
|
||||
List(toCustomType("Custom"), scToBt(string), scToBt(u32), toCustomType("Custom3"))
|
||||
.map(None -> _),
|
||||
List(toCustomType("Custom2"), scToBt(string))
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -11,10 +11,10 @@ class CallArrowSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
import AquaSpec._
|
||||
|
||||
"func calls" should "parse func()" in {
|
||||
parseExpr("func()") should be(CallArrowExpr[Id](None, None, toName("func"), List()))
|
||||
parseExpr("func()") should be(CallArrowExpr[Id](Nil, None, toName("func"), List()))
|
||||
parseExpr("Ab.func(arg)") should be(
|
||||
CallArrowExpr[Id](
|
||||
None,
|
||||
Nil,
|
||||
Some(toAb("Ab")),
|
||||
Name[Id]("func"),
|
||||
List(VarLambda[Id](toName("arg")))
|
||||
@ -23,7 +23,7 @@ class CallArrowSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
|
||||
parseExpr("func(arg.doSomething)") should be(
|
||||
CallArrowExpr[Id](
|
||||
None,
|
||||
Nil,
|
||||
None,
|
||||
Name[Id]("func"),
|
||||
List(toVarLambda("arg", List("doSomething")))
|
||||
@ -32,7 +32,7 @@ class CallArrowSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
|
||||
parseExpr("func(arg.doSomething.and.doSomethingElse)") should be(
|
||||
CallArrowExpr[Id](
|
||||
None,
|
||||
Nil,
|
||||
None,
|
||||
Name[Id]("func"),
|
||||
List(toVarLambda("arg", List("doSomething", "and", "doSomethingElse")))
|
||||
@ -41,7 +41,7 @@ class CallArrowSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
|
||||
parseExpr("func(arg.doSomething.and.doSomethingElse)") should be(
|
||||
CallArrowExpr[Id](
|
||||
None,
|
||||
Nil,
|
||||
None,
|
||||
Name[Id]("func"),
|
||||
List(toVarLambda("arg", List("doSomething", "and", "doSomethingElse")))
|
||||
@ -50,7 +50,7 @@ class CallArrowSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
|
||||
parseExpr("Ab.func(arg.doSomething.and.doSomethingElse, arg2.someFunc)") should be(
|
||||
CallArrowExpr[Id](
|
||||
None,
|
||||
Nil,
|
||||
Some(toAb("Ab")),
|
||||
Name[Id]("func"),
|
||||
List(
|
||||
@ -62,7 +62,18 @@ class CallArrowSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
|
||||
parseExpr("x <- func(arg.doSomething)") should be(
|
||||
CallArrowExpr[Id](
|
||||
Some(toName("x")),
|
||||
List(toName("x")),
|
||||
None,
|
||||
Name[Id]("func"),
|
||||
List(
|
||||
toVarLambda("arg", List("doSomething"))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
parseExpr("x, y, z <- func(arg.doSomething)") should be(
|
||||
CallArrowExpr[Id](
|
||||
toName("x") :: toName("y") :: toName("z") :: Nil,
|
||||
None,
|
||||
Name[Id]("func"),
|
||||
List(
|
||||
|
@ -20,7 +20,7 @@ class CoExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
Chain(
|
||||
Cofree[Chain, Expr[Id]](
|
||||
CallArrowExpr(
|
||||
Some(AquaSpec.toName("x")),
|
||||
List(AquaSpec.toName("x")),
|
||||
None,
|
||||
AquaSpec.toName("y"),
|
||||
Nil
|
||||
|
@ -7,7 +7,7 @@ import aqua.parser.lexer.{ArrowTypeToken, BasicTypeToken, EqOp, Literal, Token,
|
||||
import aqua.parser.lift.LiftParser.Implicits.idLiftParser
|
||||
import aqua.types.ScalarType.*
|
||||
import cats.Id
|
||||
import cats.data.Chain
|
||||
import cats.data.{Chain, NonEmptyList}
|
||||
import cats.free.Cofree
|
||||
import cats.syntax.foldable.*
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
@ -21,43 +21,43 @@ class FuncExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
|
||||
"func header" should "parse" in {
|
||||
funcExpr("func some() -> bool") should be(
|
||||
FuncExpr("some", List(), Some(bool: BasicTypeToken[Id]), None)
|
||||
FuncExpr("some", toNamedArrow(Nil, List(bool: BasicTypeToken[Id])), Nil)
|
||||
)
|
||||
funcExpr("func some()") should be(FuncExpr("some", List(), None, None))
|
||||
funcExpr("func some()") should be(FuncExpr("some", toNamedArrow(Nil, Nil), Nil))
|
||||
|
||||
val arrowToken =
|
||||
ArrowTypeToken[Id]((), List(BasicTypeToken[Id](u8)), Some(BasicTypeToken[Id](bool)))
|
||||
ArrowTypeToken[Id]((), List(None -> BasicTypeToken[Id](u8)), List(BasicTypeToken[Id](bool)))
|
||||
funcExpr("func some(peer: PeerId, other: u8 -> bool)") should be(
|
||||
FuncExpr(
|
||||
toName("some"),
|
||||
List(toCustomArg("peer", "PeerId"), toArg("other", arrowToken)),
|
||||
None,
|
||||
None
|
||||
toNamedArrow(("peer" -> toCustomType("PeerId")) :: ("other" -> arrowToken) :: Nil, Nil),
|
||||
Nil
|
||||
)
|
||||
)
|
||||
|
||||
val arrowToken2 =
|
||||
ArrowTypeToken[Id](
|
||||
(),
|
||||
List(BasicTypeToken[Id](u32), BasicTypeToken[Id](u64)),
|
||||
Some(BasicTypeToken[Id](bool))
|
||||
List(None -> BasicTypeToken[Id](u32), None -> BasicTypeToken[Id](u64)),
|
||||
List(BasicTypeToken[Id](bool))
|
||||
)
|
||||
funcExpr("func some(peer: PeerId, other: u32, u64 -> bool)") should be(
|
||||
FuncExpr(
|
||||
toName("some"),
|
||||
List(toCustomArg("peer", "PeerId"), toArg("other", arrowToken2)),
|
||||
None,
|
||||
None
|
||||
toNamedArrow(("peer" -> toCustomType("PeerId")) :: ("other" -> arrowToken2) :: Nil, Nil),
|
||||
Nil
|
||||
)
|
||||
)
|
||||
|
||||
val arrowToken3 = ArrowTypeToken[Id]((), List(BasicTypeToken[Id](u32)), None)
|
||||
funcExpr("func getTime(peer: PeerId, ret: u32 -> ()) -> string") should be(
|
||||
val arrowToken3 = ArrowTypeToken[Id]((), List(None -> BasicTypeToken[Id](u32)), Nil)
|
||||
funcExpr("func getTime(peer: PeerId, ret: u32 -> ()) -> string, u32") should be(
|
||||
FuncExpr(
|
||||
toName("getTime"),
|
||||
List(toCustomArg("peer", "PeerId"), toArg("ret", arrowToken3)),
|
||||
Some(BasicTypeToken[Id](string)),
|
||||
None
|
||||
toNamedArrow(
|
||||
("peer" -> toCustomType("PeerId")) :: ("ret" -> arrowToken3) :: Nil,
|
||||
BasicTypeToken[Id](string) :: BasicTypeToken[Id](u32) :: Nil
|
||||
),
|
||||
Nil
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -88,7 +88,7 @@ class FuncExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
| call(true)""".stripMargin
|
||||
|
||||
val tree = FuncExpr.ast[Id]().parseAll(script).value.toEither.value
|
||||
val funcBody = checkHeadGetTail(tree, FuncExpr("a", Nil, None, None), 1).toList
|
||||
val funcBody = checkHeadGetTail(tree, FuncExpr("a", toNamedArrow(Nil, Nil), Nil), 1).toList
|
||||
|
||||
val ifBody =
|
||||
checkHeadGetTail(
|
||||
@ -98,10 +98,10 @@ class FuncExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
).toList
|
||||
|
||||
ifBody.head.head should be(
|
||||
CallArrowExpr(Some(toName("x")), Some(toAb("Ab")), "func", Nil)
|
||||
CallArrowExpr(List(toName("x")), Some(toAb("Ab")), "func", Nil)
|
||||
)
|
||||
ifBody(1).head should be(AbilityIdExpr(toAb("Peer"), toStr("some id")))
|
||||
ifBody(2).head should be(CallArrowExpr(None, None, "call", List(toBool(true))))
|
||||
ifBody(2).head should be(CallArrowExpr(Nil, None, "call", List(toBool(true))))
|
||||
|
||||
}
|
||||
|
||||
@ -161,22 +161,25 @@ class FuncExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
// Local service
|
||||
qTree.d() shouldBe ServiceExpr(toAb("Local"), Some(toStr("local")))
|
||||
qTree.d() shouldBe ArrowTypeExpr("gt", toArrowType(Nil, Some(scToBt(bool))))
|
||||
qTree.d() shouldBe FuncExpr("tryGen", Nil, Some(scToBt(bool)), Some("v": VarLambda[Id]))
|
||||
qTree.d() shouldBe FuncExpr(
|
||||
"tryGen",
|
||||
toNamedArrow(Nil, scToBt(bool) :: Nil),
|
||||
List("v": VarLambda[Id])
|
||||
)
|
||||
qTree.d() shouldBe OnExpr(toStr("deeper"), List(toStr("deep")))
|
||||
qTree.d() shouldBe CallArrowExpr(Some("v"), Some(toAb("Local")), "gt", Nil)
|
||||
qTree.d() shouldBe ReturnExpr(toVar("v"))
|
||||
qTree.d() shouldBe CallArrowExpr(List("v"), Some(toAb("Local")), "gt", Nil)
|
||||
qTree.d() shouldBe ReturnExpr(NonEmptyList.one(toVar("v")))
|
||||
// genC function
|
||||
qTree.d() shouldBe FuncExpr(
|
||||
"genC",
|
||||
List(toArgSc("val", string)),
|
||||
Some(boolSc),
|
||||
Some("two": VarLambda[Id])
|
||||
toNamedArrow(("val" -> string) :: Nil, boolSc :: Nil),
|
||||
List("two": VarLambda[Id])
|
||||
)
|
||||
qTree.d() shouldBe CallArrowExpr(Some("one"), Some(toAb("Local")), "gt", List())
|
||||
qTree.d() shouldBe CallArrowExpr(List("one"), Some(toAb("Local")), "gt", List())
|
||||
qTree.d() shouldBe OnExpr(toStr("smth"), List(toStr("else")))
|
||||
qTree.d() shouldBe CallArrowExpr(Some("two"), None, "tryGen", List())
|
||||
qTree.d() shouldBe CallArrowExpr(Some("three"), Some(toAb("Local")), "gt", List())
|
||||
qTree.d() shouldBe ReturnExpr(toVar("two"))
|
||||
qTree.d() shouldBe CallArrowExpr(List("two"), None, "tryGen", List())
|
||||
qTree.d() shouldBe CallArrowExpr(List("three"), Some(toAb("Local")), "gt", List())
|
||||
qTree.d() shouldBe ReturnExpr(NonEmptyList.one(toVar("two")))
|
||||
/* TODO this is semantics, not parser test
|
||||
|
||||
val f =
|
||||
|
@ -20,7 +20,7 @@ class ParExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
Chain(
|
||||
Cofree[Chain, Expr[Id]](
|
||||
CallArrowExpr(
|
||||
Some(AquaSpec.toName("x")),
|
||||
List(AquaSpec.toName("x")),
|
||||
None,
|
||||
AquaSpec.toName("y"),
|
||||
Nil
|
||||
|
@ -3,6 +3,7 @@ package aqua.parser
|
||||
import aqua.AquaSpec
|
||||
import aqua.parser.expr.ReturnExpr
|
||||
import cats.Id
|
||||
import cats.data.NonEmptyList
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
@ -11,7 +12,7 @@ class ReturnExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
|
||||
"return" should "be parsed" in {
|
||||
parseReturn("<- true") should be(
|
||||
ReturnExpr[Id](toBool(true))
|
||||
ReturnExpr[Id](NonEmptyList.one(toBool(true)))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -20,38 +20,56 @@ class TypeTokenSpec extends AnyFlatSpec with Matchers with EitherValues {
|
||||
}
|
||||
|
||||
"Arrow type" should "parse" in {
|
||||
val arrowdef = ArrowTypeToken.`arrowdef`[Id](DataTypeToken.`datatypedef`[Id])
|
||||
val arrowWithNames = ArrowTypeToken.`arrowWithNames`[Id](DataTypeToken.`datatypedef`[Id])
|
||||
|
||||
ArrowTypeToken.`arrowdef`.parseAll("-> B").value should be(
|
||||
ArrowTypeToken[Id]((), Nil, Some(CustomTypeToken[Id]("B")))
|
||||
arrowdef.parseAll("-> B").value should be(
|
||||
ArrowTypeToken[Id]((), Nil, List(CustomTypeToken[Id]("B")))
|
||||
)
|
||||
ArrowTypeToken.`arrowdef`.parseAll("A -> B").value should be(
|
||||
ArrowTypeToken[Id]((), CustomTypeToken[Id]("A") :: Nil, Some(CustomTypeToken[Id]("B")))
|
||||
)
|
||||
|
||||
ArrowTypeToken.`arrowWithNames`.parseAll("(a: A) -> B").value should be(
|
||||
ArrowTypeToken[Id]((), CustomTypeToken[Id]("A") :: Nil, Some(CustomTypeToken[Id]("B")))
|
||||
)
|
||||
|
||||
ArrowTypeToken.`arrowdef`.parseAll("u32 -> Boo").value should be(
|
||||
ArrowTypeToken[Id]((), (u32: BasicTypeToken[Id]) :: Nil, Some(CustomTypeToken[Id]("Boo")))
|
||||
)
|
||||
TypeToken.`typedef`.parseAll("u32 -> ()").value should be(
|
||||
ArrowTypeToken[Id]((), (u32: BasicTypeToken[Id]) :: Nil, None)
|
||||
)
|
||||
ArrowTypeToken.`arrowdef`.parseAll("A, u32 -> B").value should be(
|
||||
arrowdef.parseAll("A -> B").value should be(
|
||||
ArrowTypeToken[Id](
|
||||
(),
|
||||
CustomTypeToken[Id]("A") :: (u32: BasicTypeToken[Id]) :: Nil,
|
||||
Some(CustomTypeToken[Id]("B"))
|
||||
(None -> CustomTypeToken[Id]("A")) :: Nil,
|
||||
List(CustomTypeToken[Id]("B"))
|
||||
)
|
||||
)
|
||||
ArrowTypeToken.`arrowdef`.parseAll("[]Absolutely, u32 -> B").value should be(
|
||||
|
||||
arrowWithNames.parseAll("(a: A) -> B").value should be(
|
||||
ArrowTypeToken[Id](
|
||||
(),
|
||||
ArrayTypeToken[Id]((), CustomTypeToken[Id]("Absolutely")) :: (u32: BasicTypeToken[
|
||||
(Some(Name[Id]("a")) -> CustomTypeToken[Id]("A")) :: Nil,
|
||||
List(CustomTypeToken[Id]("B"))
|
||||
)
|
||||
)
|
||||
|
||||
arrowdef.parseAll("u32 -> Boo").value should be(
|
||||
ArrowTypeToken[Id](
|
||||
(),
|
||||
(None -> (u32: BasicTypeToken[Id])) :: Nil,
|
||||
List(CustomTypeToken[Id]("Boo"))
|
||||
)
|
||||
)
|
||||
TypeToken.`typedef`.parseAll("u32 -> ()").value should be(
|
||||
ArrowTypeToken[Id]((), (None -> (u32: BasicTypeToken[Id])) :: Nil, Nil)
|
||||
)
|
||||
arrowdef.parseAll("A, u32 -> B").value should be(
|
||||
ArrowTypeToken[Id](
|
||||
(),
|
||||
(None -> CustomTypeToken[Id]("A")) :: (None -> (u32: BasicTypeToken[Id])) :: Nil,
|
||||
List(CustomTypeToken[Id]("B"))
|
||||
)
|
||||
)
|
||||
arrowdef.parseAll("[]Absolutely, u32 -> B, C").value should be(
|
||||
ArrowTypeToken[Id](
|
||||
(),
|
||||
(Option.empty[Name[Id]] -> ArrayTypeToken[Id](
|
||||
(),
|
||||
CustomTypeToken[Id]("Absolutely")
|
||||
)) :: (Option.empty[Name[Id]] -> (u32: BasicTypeToken[
|
||||
Id
|
||||
]) :: Nil,
|
||||
Some(CustomTypeToken[Id]("B"))
|
||||
])) :: Nil,
|
||||
CustomTypeToken[Id]("B") ::
|
||||
CustomTypeToken[Id]("C") :: Nil
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -29,21 +29,24 @@ class CallArrowSem[F[_]](val expr: CallArrowExpr[F]) extends AnyVal {
|
||||
N: NamesAlgebra[F, Alg],
|
||||
T: TypesAlgebra[F, Alg],
|
||||
V: ValuesAlgebra[F, Alg]
|
||||
): Free[Alg, (List[ValueModel], Option[Type])] =
|
||||
V.checkArguments(expr.funcName, at, args) >> variable
|
||||
.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].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]) =>
|
||||
Traverse[List].traverse(args)(V.valueToModel).map(_.flatten).map(_ -> v)
|
||||
): Free[Alg, (List[ValueModel], List[Type])] =
|
||||
V.checkArguments(expr.funcName, at, args) >> variables
|
||||
.foldLeft(freeUnit[Alg].as((List.empty[Type], at.codomain.toList)))((f, exportVar) =>
|
||||
f.flatMap {
|
||||
case (exports, Nil) =>
|
||||
freeUnit[Alg].as(exports -> Nil)
|
||||
|
||||
case (exports, resType :: codom) =>
|
||||
N.read(exportVar, mustBeDefined = false).flatMap {
|
||||
case Some(t @ StreamType(st)) =>
|
||||
T.ensureTypeMatches(exportVar, st, resType).as((t :: exports, codom))
|
||||
case _ =>
|
||||
N.define(exportVar, resType).as((resType :: exports, codom))
|
||||
}
|
||||
}
|
||||
)
|
||||
.map(_._1) >>= { (v: List[Type]) =>
|
||||
Traverse[List].traverse(args)(V.valueToModel).map(_.flatten -> v.reverse)
|
||||
}
|
||||
|
||||
private def toModel[Alg[_]](implicit
|
||||
@ -74,30 +77,30 @@ class CallArrowSem[F[_]](val expr: CallArrowExpr[F]) extends AnyVal {
|
||||
T: TypesAlgebra[F, Alg],
|
||||
V: ValuesAlgebra[F, Alg]
|
||||
): Free[Alg, FuncOp] = {
|
||||
checkArgsRes(arrowType).flatMap { case (argsResolved, tOp) =>
|
||||
((variable, tOp) match {
|
||||
case (Some(v), Some(t)) =>
|
||||
Free.pure[Alg, Option[Call.Export]](Option(Call.Export(v.value, t)))
|
||||
case (Some(v), None) =>
|
||||
T.expectNoExport(v).map(_ => None)
|
||||
case _ =>
|
||||
Free.pure[Alg, Option[Call.Export]](None)
|
||||
|
||||
}).map(maybeExport =>
|
||||
FuncOp.leaf(serviceId match {
|
||||
case Some(sid) =>
|
||||
CallServiceTag(
|
||||
serviceId = sid,
|
||||
funcName = funcName.value,
|
||||
Call(argsResolved, maybeExport.toList)
|
||||
)
|
||||
case None =>
|
||||
CallArrowTag(
|
||||
funcName = funcName.value,
|
||||
Call(argsResolved, maybeExport.toList)
|
||||
)
|
||||
})
|
||||
)
|
||||
checkArgsRes(arrowType).flatMap { (argsResolved, resTypes) =>
|
||||
variables
|
||||
.drop(arrowType.codomain.length)
|
||||
.headOption
|
||||
.fold(
|
||||
Free.pure((variables zip resTypes).map { case (v, t) =>
|
||||
Call.Export(v.value, t)
|
||||
})
|
||||
)(T.expectNoExport(_).as(Nil))
|
||||
.map(maybeExport =>
|
||||
FuncOp.leaf(serviceId match {
|
||||
case Some(sid) =>
|
||||
CallServiceTag(
|
||||
serviceId = sid,
|
||||
funcName = funcName.value,
|
||||
Call(argsResolved, maybeExport)
|
||||
)
|
||||
case None =>
|
||||
CallArrowTag(
|
||||
funcName = funcName.value,
|
||||
Call(argsResolved, maybeExport)
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,35 +29,40 @@ class FuncSem[F[_]](val expr: FuncExpr[F]) extends AnyVal {
|
||||
A.beginScope(name) >> Applicative[Free[Alg, *]]
|
||||
.product(
|
||||
// Collect argument types, define local variables
|
||||
args
|
||||
arrowTypeExpr.args
|
||||
.foldLeft(
|
||||
// Begin scope -- for mangling
|
||||
N.beginScope(name).as[Chain[(String, Type)]](Chain.empty)
|
||||
) { case (f, Arg(argName, argType)) =>
|
||||
// Resolve arg type, remember it
|
||||
f.flatMap(acc =>
|
||||
T.resolveType(argType).flatMap {
|
||||
case Some(t: ArrowType) =>
|
||||
N.defineArrow(argName, t, isRoot = false).as(acc.append(argName.value -> t))
|
||||
case Some(t) =>
|
||||
N.define(argName, t).as(acc.append(argName.value -> t))
|
||||
case None =>
|
||||
Free.pure(acc)
|
||||
}
|
||||
)
|
||||
) {
|
||||
case (f, (Some(argName), argType)) =>
|
||||
// Resolve arg type, remember it
|
||||
f.flatMap(acc =>
|
||||
T.resolveType(argType).flatMap {
|
||||
case Some(t: ArrowType) =>
|
||||
N.defineArrow(argName, t, isRoot = false).as(acc.append(argName.value -> t))
|
||||
case Some(t) =>
|
||||
N.define(argName, t).as(acc.append(argName.value -> t))
|
||||
case None =>
|
||||
Free.pure(acc)
|
||||
}
|
||||
)
|
||||
// Unnamed argument
|
||||
case (f, _) => f
|
||||
}
|
||||
.map(_.toList),
|
||||
// Resolve return type
|
||||
ret.fold(Free.pure[Alg, Option[Type]](None))(T.resolveType(_))
|
||||
ret.foldLeft(Free.pure[Alg, List[Type]](Nil))((f, t) =>
|
||||
f.flatMap(ts => T.resolveType(t).map(ts.prependedAll))
|
||||
)
|
||||
)
|
||||
.map(argsAndRes =>
|
||||
ArrowType(ProductType.labelled(argsAndRes._1), ProductType(argsAndRes._2.toList))
|
||||
ArrowType(ProductType.labelled(argsAndRes._1), ProductType(argsAndRes._2.reverse))
|
||||
)
|
||||
|
||||
def generateFuncModel[Alg[_]](funcArrow: ArrowType, retModel: List[ValueModel], body: FuncOp)(
|
||||
implicit N: NamesAlgebra[F, Alg]
|
||||
): Free[Alg, Model] = {
|
||||
val argNames = args.map(_.name.value)
|
||||
val argNames = arrowTypeExpr.args.collect { case (Some(n), _) => n.value }
|
||||
|
||||
val model = FuncModel(
|
||||
name = name.value,
|
||||
@ -80,27 +85,27 @@ class FuncSem[F[_]](val expr: FuncExpr[F]) extends AnyVal {
|
||||
A: AbilitiesAlgebra[F, Alg]
|
||||
): Free[Alg, Model] =
|
||||
// Check return value type
|
||||
((funcArrow.res, retValue) match {
|
||||
case (Some(t), Some(v)) =>
|
||||
V.valueToModel(v)
|
||||
.flatTap {
|
||||
case Some(vt) => T.ensureTypeMatches(v, t, vt.lastType).void
|
||||
case None => Free.pure[Alg, Unit](())
|
||||
}
|
||||
.map(_.toList)
|
||||
case (_, _) =>
|
||||
Free.pure[Alg, List[ValueModel]](Nil)
|
||||
|
||||
// Erase arguments and internal variables
|
||||
}).flatMap(retModel =>
|
||||
A.endScope() >> N.endScope() >> (bodyGen match {
|
||||
case body: FuncOp if ret.isDefined == retValue.isDefined =>
|
||||
generateFuncModel[Alg](funcArrow, retModel, body)
|
||||
case ReturnModel =>
|
||||
generateFuncModel[Alg](funcArrow, retModel, FuncOps.empty)
|
||||
case m => Free.pure[Alg, Model](Model.error("Function body is not a funcOp, it's " + m))
|
||||
((funcArrow.codomain.toList zip retValue)
|
||||
.foldLeft(Free.pure[Alg, List[ValueModel]](Nil)) { case (f, (t, v)) =>
|
||||
f.flatMap(vs =>
|
||||
V.valueToModel(v)
|
||||
.flatTap {
|
||||
case Some(vt) => T.ensureTypeMatches(v, t, vt.lastType).void
|
||||
case None => Free.pure[Alg, Unit](())
|
||||
}
|
||||
.map(vs.prependedAll)
|
||||
)
|
||||
})
|
||||
)
|
||||
.flatMap(retModel =>
|
||||
// Erase arguments and internal variables
|
||||
A.endScope() >> N.endScope() >> (bodyGen match {
|
||||
case body: FuncOp if ret.length == retValue.length =>
|
||||
generateFuncModel[Alg](funcArrow, retModel, body)
|
||||
case ReturnModel =>
|
||||
generateFuncModel[Alg](funcArrow, retModel, FuncOps.empty)
|
||||
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],
|
||||
|
@ -5,9 +5,10 @@ import aqua.parser.expr.ReturnExpr
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.ValuesAlgebra
|
||||
import cats.syntax.functor._
|
||||
import cats.syntax.traverse._
|
||||
|
||||
class ReturnSem[F[_]](val expr: ReturnExpr[F]) extends AnyVal {
|
||||
|
||||
def program[Alg[_]](implicit V: ValuesAlgebra[F, Alg]): Prog[Alg, Model] =
|
||||
V.resolveType(expr.value) as (ReturnModel: Model)
|
||||
expr.values.traverse(V.resolveType) as (ReturnModel: Model)
|
||||
}
|
||||
|
@ -59,23 +59,33 @@ case class TypesState[F[_]](
|
||||
case ctt: CustomTypeToken[F] => strict.get(ctt.value)
|
||||
case btt: BasicTypeToken[F] => Some(btt.value)
|
||||
case ArrowTypeToken(_, args, res) =>
|
||||
val strictArgs = args.map(resolveTypeToken).collect { case Some(dt: DataType) =>
|
||||
val strictArgs = args.map(_._2).map(resolveTypeToken).collect { case Some(dt: DataType) =>
|
||||
dt
|
||||
}
|
||||
val strictRes = res.flatMap(resolveTypeToken).collect { case dt: DataType =>
|
||||
val strictRes: List[DataType] = res.flatMap(resolveTypeToken).collect { case dt: DataType =>
|
||||
dt
|
||||
}
|
||||
Option.when(strictRes.isDefined == res.isDefined && strictArgs.length == args.length)(
|
||||
Option.when(strictRes.length == res.length && strictArgs.length == args.length)(
|
||||
ArrowType(ProductType(strictArgs), ProductType(strictRes.toList))
|
||||
)
|
||||
}
|
||||
|
||||
def resolveArrowDef(ad: ArrowTypeToken[F]): ValidatedNec[(Token[F], String), ArrowType] =
|
||||
ad.resType.flatMap(resolveTypeToken) match {
|
||||
case resType if resType.isDefined == ad.resType.isDefined =>
|
||||
val (errs, argTypes) = ad.argTypes
|
||||
.map(tt => resolveTypeToken(tt).toRight(tt -> s"Type unresolved"))
|
||||
.foldLeft[(Chain[(Token[F], String)], Chain[Type])]((Chain.empty, Chain.empty)) {
|
||||
def resolveArrowDef(ad: ArrowTypeToken[F]): ValidatedNec[(Token[F], String), ArrowType] = {
|
||||
val resType = ad.res.map(resolveTypeToken)
|
||||
|
||||
NonEmptyChain
|
||||
.fromChain(Chain.fromSeq(ad.res.zip(resType).collect { case (dt, None) =>
|
||||
dt -> "Cannot resolve the result type"
|
||||
}))
|
||||
.fold[ValidatedNec[(Token[F], String), ArrowType]] {
|
||||
val (errs, argTypes) = ad.args.map { (argName, tt) =>
|
||||
resolveTypeToken(tt)
|
||||
.toRight(tt -> s"Type unresolved")
|
||||
.map(argName.map(_.value) -> _)
|
||||
}
|
||||
.foldLeft[(Chain[(Token[F], String)], Chain[(Option[String], Type)])](
|
||||
(Chain.empty, Chain.empty)
|
||||
) {
|
||||
case ((errs, argTypes), Right(at)) => (errs, argTypes.append(at))
|
||||
case ((errs, argTypes), Left(e)) => (errs.append(e), argTypes)
|
||||
}
|
||||
@ -83,12 +93,15 @@ case class TypesState[F[_]](
|
||||
NonEmptyChain
|
||||
.fromChain(errs)
|
||||
.fold[ValidatedNec[(Token[F], String), ArrowType]](
|
||||
Valid(ArrowType(ProductType(argTypes.toList), ProductType(resType.toList)))
|
||||
Valid(
|
||||
ArrowType(
|
||||
ProductType.maybeLabelled(argTypes.toList),
|
||||
ProductType(resType.flatten.toList)
|
||||
)
|
||||
)
|
||||
)(Invalid(_))
|
||||
|
||||
case _ =>
|
||||
Invalid(NonEmptyChain.one(ad.resType.getOrElse(ad) -> "Cannot resolve the result type"))
|
||||
}
|
||||
}(Invalid(_))
|
||||
}
|
||||
|
||||
def resolveOps(
|
||||
rootT: Type,
|
||||
|
@ -70,7 +70,7 @@ object CompareTypes {
|
||||
) -1.0
|
||||
else NaN
|
||||
|
||||
private def compareProducts(l: ProductType, r: ProductType): Double = (l, r) match {
|
||||
private def compareProducts(l: ProductType, r: ProductType): Double = ((l, r): @unchecked) match {
|
||||
case (NilType, NilType) => 0.0
|
||||
case (_: ConsType, NilType) => -1.0
|
||||
case (NilType, _: ConsType) => 1.0
|
||||
|
@ -59,6 +59,14 @@ object ProductType {
|
||||
case _ => NilType
|
||||
}
|
||||
|
||||
def maybeLabelled(types: List[(Option[String], Type)]): ProductType = types match {
|
||||
case (Some(l), h) :: t =>
|
||||
ConsType.cons(l, h, ProductType.maybeLabelled(t))
|
||||
case (None, h) :: t =>
|
||||
ConsType.cons(h, ProductType.maybeLabelled(t))
|
||||
case _ => NilType
|
||||
}
|
||||
|
||||
def labelled(types: List[(String, Type)]): ProductType = types match {
|
||||
case (l, h) :: t =>
|
||||
ConsType.cons(l, h, ProductType.labelled(t))
|
||||
@ -181,24 +189,11 @@ case class StructType(name: String, fields: NonEmptyMap[String, Type]) extends D
|
||||
*/
|
||||
case class ArrowType(domain: ProductType, codomain: ProductType) extends Type {
|
||||
|
||||
@deprecated(
|
||||
"Use .domain to get arguments, add .args helper to the typed object, if needed",
|
||||
"5.08.2021"
|
||||
)
|
||||
def args: List[Type] = domain.toList
|
||||
|
||||
@deprecated(
|
||||
"Use .codomain to get results, add .res helper to the typed object, if needed; consider multi-value return",
|
||||
"5.08.2021"
|
||||
)
|
||||
def res: Option[Type] = codomain.uncons.map(_._1)
|
||||
|
||||
@deprecated(
|
||||
"Replace with this function's body",
|
||||
"5.08.2021"
|
||||
)
|
||||
def acceptsAsArguments(valueTypes: List[Type]): Boolean =
|
||||
domain.acceptsValueOf(ProductType(valueTypes))
|
||||
lazy val res: Option[Type] = codomain.toList match {
|
||||
case Nil => None
|
||||
case a :: Nil => Some(a)
|
||||
case _ => Some(codomain)
|
||||
}
|
||||
|
||||
override def toString: String =
|
||||
s"$domain -> $codomain"
|
||||
|
Loading…
Reference in New Issue
Block a user