Return many values from a single function (#229)

This commit is contained in:
Dmitry Kurinskiy 2021-08-09 21:33:55 +03:00 committed by GitHub
parent cd30ff8e8c
commit 3eb3ecc221
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 400 additions and 276 deletions

View File

@ -49,7 +49,7 @@ jobs:
- name: Check .jar exists - name: Check .jar exists
run: | 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" stat "$JAR"
echo "JAR=$JAR" >> $GITHUB_ENV echo "JAR=$JAR" >> $GITHUB_ENV

View File

@ -1,16 +1,15 @@
data DT: service Getter("test"):
field: string createStr: u32 -> string
service DTGetter("get-dt"): service OpO("op"):
get_dt(s: string) -> DT identity: string -> string
func use_name1(name: string) -> string: -- a question mark means that this constant could be rewritten before this definition
results <- DTGetter.get_dt(name) const anotherConst ?= "default-str"
<- results.field const uniqueConst ?= 5
func use_name2(name: string) -> []string: func callConstant() -> []string, u8:
results: *string res: *string
results <- use_name1(name) res <- Getter.createStr(uniqueConst)
results <- use_name1(name) res <- OpO.identity(anotherConst)
results <- use_name1(name) <- res, 5
<- results

View File

@ -40,7 +40,7 @@ object AirGen extends LogSupport {
def opsToSingle(ops: Chain[AirGen]): AirGen = ops.toList match { def opsToSingle(ops: Chain[AirGen]): AirGen = ops.toList match {
case Nil => NullGen case Nil => NullGen
case h :: Nil => h case h :: Nil => h
case list => list.reduceLeft(SeqGen) case list => list.reduceLeft(SeqGen(_, _))
} }
private def folder(op: ResolvedOp, ops: Chain[AirGen]): Eval[AirGen] = private def folder(op: ResolvedOp, ops: Chain[AirGen]): Eval[AirGen] =
@ -48,12 +48,12 @@ object AirGen extends LogSupport {
// case mt: MetaTag => // case mt: MetaTag =>
// folder(mt.op, ops).map(ag => mt.comment.fold(ag)(CommentGen(_, ag))) // folder(mt.op, ops).map(ag => mt.comment.fold(ag)(CommentGen(_, ag)))
case SeqRes => case SeqRes =>
Eval later ops.toList.reduceLeftOption(SeqGen).getOrElse(NullGen) Eval later ops.toList.reduceLeftOption(SeqGen(_, _)).getOrElse(NullGen)
case ParRes => case ParRes =>
Eval later (ops.toList match { Eval later (ops.toList match {
case o :: Nil => ParGen(o, NullGen) case o :: Nil => ParGen(o, NullGen)
case _ => case _ =>
ops.toList.reduceLeftOption(ParGen).getOrElse { ops.toList.reduceLeftOption(ParGen(_, _)).getOrElse {
warn("ParRes with no children converted to Null") warn("ParRes with no children converted to Null")
NullGen NullGen
} }
@ -62,7 +62,7 @@ object AirGen extends LogSupport {
Eval later (ops.toList match { Eval later (ops.toList match {
case o :: Nil => XorGen(o, NullGen) case o :: Nil => XorGen(o, NullGen)
case _ => case _ =>
ops.toList.reduceLeftOption(XorGen).getOrElse { ops.toList.reduceLeftOption(XorGen(_, _)).getOrElse {
warn("XorRes with no children converted to Null") warn("XorRes with no children converted to Null")
NullGen NullGen
} }

View File

@ -27,6 +27,18 @@ case class JavaScriptFunc(func: FuncCallable) {
| opt = opt[0]; | opt = opt[0];
| } | }
| return resolve(opt);""".stripMargin | 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 _ => case _ =>
""" const [res] = args; """ const [res] = args;
| resolve(res);""".stripMargin | resolve(res);""".stripMargin
@ -51,18 +63,15 @@ case class JavaScriptFunc(func: FuncCallable) {
val value = s"$argName(${argsCallToJs( val value = s"$argName(${argsCallToJs(
at 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;});""" s"""h.on('${conf.callbackService}', '$argName', (args) => {$expr;});"""
} }
.mkString("\n") .mkString("\n")
// TODO support multi-return
val returnCallback = func.arrowType.codomain.uncons val returnCallback = func.arrowType.codomain.uncons
.map(_._1) .map(_ => genReturnCallback(func.arrowType.codomain, conf.callbackService, conf.respFuncName))
.map(t => genReturnCallback(t, conf.callbackService, conf.respFuncName))
.getOrElse("") .getOrElse("")
// TODO support multi-return
val returnVal = val returnVal =
func.ret.headOption.fold("Promise.race([promise, Promise.resolve()])")(_ => "promise") func.ret.headOption.fold("Promise.race([promise, Promise.resolve()])")(_ => "promise")

View File

@ -35,6 +35,18 @@ case class TypeScriptFunc(func: FuncCallable) {
| opt = opt[0]; | opt = opt[0];
| } | }
| return resolve(opt);""".stripMargin | 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 _ => case _ =>
""" const [res] = args; """ const [res] = args;
| resolve(res);""".stripMargin | resolve(res);""".stripMargin
@ -51,8 +63,9 @@ case class TypeScriptFunc(func: FuncCallable) {
val tsAir = FuncAirGen(func).generateAir(conf) val tsAir = FuncAirGen(func).generateAir(conf)
// TODO: support multi return // TODO: support multi return
val retType = func.arrowType.codomain.uncons val retType =
.map(_._1) if (func.arrowType.codomain.length > 1) Some(func.arrowType.codomain)
else func.arrowType.codomain.uncons.map(_._1)
val retTypeTs = retType val retTypeTs = retType
.fold("void")(typeToTs) .fold("void")(typeToTs)
@ -133,8 +146,10 @@ object TypeScriptFunc {
case OptionType(t) => typeToTs(t) + " | null" case OptionType(t) => typeToTs(t) + " | null"
case ArrayType(t) => typeToTs(t) + "[]" case ArrayType(t) => typeToTs(t) + "[]"
case StreamType(t) => typeToTs(t) + "[]" case StreamType(t) => typeToTs(t) + "[]"
case pt: StructType => case pt: ProductType =>
s"{${pt.fields.map(typeToTs).toNel.map(kv => kv._1 + ":" + kv._2).toList.mkString(";")}}" "[" + 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 st: ScalarType if ScalarType.number(st) => "number"
case ScalarType.bool => "boolean" case ScalarType.bool => "boolean"
case ScalarType.string => "string" case ScalarType.string => "string"
@ -157,6 +172,6 @@ object TypeScriptFunc {
.mkString(", ") .mkString(", ")
def argsCallToTs(at: ArrowType): String = 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(", ")
} }

View File

@ -24,7 +24,7 @@ val cats = "org.typelevel" %% "cats-core" % catsV
name := "aqua-hll" name := "aqua-hll"
val commons = Seq( val commons = Seq(
baseAquaVersion := "0.1.12", baseAquaVersion := "0.1.13",
version := baseAquaVersion.value + "-" + sys.env.getOrElse("BUILD_NUMBER", "SNAPSHOT"), version := baseAquaVersion.value + "-" + sys.env.getOrElse("BUILD_NUMBER", "SNAPSHOT"),
scalaVersion := dottyVersion, scalaVersion := dottyVersion,
libraryDependencies ++= Seq( libraryDependencies ++= Seq(

View File

@ -1,3 +1,3 @@
package aqua.model package aqua.model
object ReturnModel extends Model {} object ReturnModel extends Model

View File

@ -16,11 +16,11 @@ case class ResolveFunc(
private val returnVar: String = "-return-" private val returnVar: String = "-return-"
def returnCallback(retModel: ValueModel): FuncOp = def returnCallback(retModel: List[ValueModel]): FuncOp =
callback( callback(
respFuncName, respFuncName,
Call( Call(
retModel :: Nil, retModel,
Nil Nil
) )
) )
@ -55,9 +55,9 @@ case class ResolveFunc(
func.arrowType.domain.toLabelledList().map(ad => VarModel(ad._1, ad._2)), 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) => Call.Export(l, t) }
) )
) :: ) :: returnType.headOption
returnType.map { case (l, t) => VarModel(l, t) } .map(_ => returnCallback(returnType.map { case (l, t) => VarModel(l, t) }))
.map(returnCallback): _* .toList: _*
) )
), ),
ArrowType(ConsType.cons(func.funcName, func.arrowType, NilType), NilType), ArrowType(ConsType.cons(func.funcName, func.arrowType, NilType), NilType),

View File

@ -1,8 +1,8 @@
package aqua.parser.expr package aqua.parser.expr
import aqua.parser.Expr import aqua.parser.Expr
import aqua.parser.lexer.Token._ import aqua.parser.lexer.Token.*
import aqua.parser.lexer.{ArrowTypeToken, Name} import aqua.parser.lexer.{ArrowTypeToken, DataTypeToken, Name}
import aqua.parser.lift.LiftParser import aqua.parser.lift.LiftParser
import cats.Comonad import cats.Comonad
import cats.parse.Parser import cats.parse.Parser
@ -14,8 +14,9 @@ object ArrowTypeExpr extends Expr.Leaf {
override def p[F[_]: LiftParser: Comonad]: Parser[ArrowTypeExpr[F]] = override def p[F[_]: LiftParser: Comonad]: Parser[ArrowTypeExpr[F]] =
(Name (Name
.p[F] ~ ((` : ` *> ArrowTypeToken.`arrowdef`[F]) | ArrowTypeToken.`arrowWithNames`)).map { .p[F] ~ ((` : ` *> ArrowTypeToken.`arrowdef`[F](
case (name, t) => DataTypeToken.`datatypedef`[F]
ArrowTypeExpr(name, t) )) | ArrowTypeToken.`arrowWithNames`(DataTypeToken.`datatypedef`[F]))).map { case (name, t) =>
ArrowTypeExpr(name, t)
} }
} }

View File

@ -8,7 +8,7 @@ import cats.Comonad
import cats.parse.{Parser => P} import cats.parse.{Parser => P}
case class CallArrowExpr[F[_]]( case class CallArrowExpr[F[_]](
variable: Option[Name[F]], variables: List[Name[F]],
ability: Option[Ability[F]], ability: Option[Ability[F]],
funcName: Name[F], funcName: Name[F],
args: List[Value[F]] args: List[Value[F]]
@ -17,12 +17,12 @@ case class CallArrowExpr[F[_]](
object CallArrowExpr extends Expr.Leaf { object CallArrowExpr extends Expr.Leaf {
override def p[F[_]: LiftParser: Comonad]: P[CallArrowExpr[F]] = override def p[F[_]: LiftParser: Comonad]: P[CallArrowExpr[F]] =
((Name.p[F] <* ` <- `).backtrack.?.with1 ~ ((comma(Name.p[F]) <* ` <- `).backtrack.?.with1 ~
((Ability.ab[F] <* `.`).?.with1 ~ ((Ability.ab[F] <* `.`).?.with1 ~
Name.p[F] ~ Name.p[F] ~
comma0(Value.`value`[F].surroundedBy(`/s*`)).between(`(` <* `/s*`, `/s*` *> `)`))).map { comma0(Value.`value`[F].surroundedBy(`/s*`)).between(`(` <* `/s*`, `/s*` *> `)`))).map {
case (variable, ((ability, funcName), args)) => case (variables, ((ability, funcName), args)) =>
CallArrowExpr(variable, ability, funcName, args) CallArrowExpr(variables.toList.flatMap(_.toList), ability, funcName, args)
} }
} }

View File

@ -1,7 +1,7 @@
package aqua.parser.expr package aqua.parser.expr
import aqua.parser.lexer.Token._ import aqua.parser.lexer.Token.*
import aqua.parser.lexer.{Arg, DataTypeToken, Name, Value} import aqua.parser.lexer.{Arg, ArrowTypeToken, DataTypeToken, Name, TypeToken, Value}
import aqua.parser.lift.LiftParser import aqua.parser.lift.LiftParser
import aqua.parser.{Ast, Expr, FuncReturnError, ParserError} import aqua.parser.{Ast, Expr, FuncReturnError, ParserError}
import cats.Comonad import cats.Comonad
@ -11,10 +11,11 @@ import cats.parse.Parser
case class FuncExpr[F[_]]( case class FuncExpr[F[_]](
name: Name[F], name: Name[F],
args: List[Arg[F]], arrowTypeExpr: ArrowTypeToken[F],
ret: Option[DataTypeToken[F]], retValue: List[Value[F]]
retValue: Option[Value[F]] ) extends Expr[F](FuncExpr, name) {
) extends Expr[F](FuncExpr, name) def ret = arrowTypeExpr.res
}
object FuncExpr extends Expr.AndIndented { object FuncExpr extends Expr.AndIndented {
@ -36,10 +37,10 @@ object FuncExpr extends Expr.AndIndented {
Nil Nil
override def p[F[_]: LiftParser: Comonad]: Parser[FuncExpr[F]] = override def p[F[_]: LiftParser: Comonad]: Parser[FuncExpr[F]] =
((`func` *> ` ` *> Name.p[F]) ((`func` *> ` ` *> Name.p[F]) ~ ArrowTypeToken.`arrowWithNames`[F](
~ comma0(Arg.p.surroundedBy(`/s*`)).between(`(` <* `/s*`, `/s*` *> `)`) TypeToken.`typedef`[F]
~ (` -> ` *> DataTypeToken.`datatypedef`).?).map { case ((name, args), ret) => )).map { case (name, arrow) =>
FuncExpr(name, args, ret, None) FuncExpr(name, arrow.copy(unit = name.unit), Nil)
} }
override def ast[F[_]: LiftParser: Comonad](): Parser[ValidatedNec[ParserError[F], Ast.Tree[F]]] = override def ast[F[_]: LiftParser: Comonad](): Parser[ValidatedNec[ParserError[F], Ast.Tree[F]]] =
@ -49,34 +50,65 @@ object FuncExpr extends Expr.AndIndented {
_.andThen(tree => _.andThen(tree =>
tree.head match { tree.head match {
case funcExpr: FuncExpr[F] => 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 { funcExpr.ret match {
case Some(ret) => case Nil =>
tree.tail.value.lastOption.map(_.head) match { // Nothing should be returned
case Some(re: ReturnExpr[F]) => maybeReturn.fold(Validated.validNec(tree))(re =>
Validated // Declared nothing, but smth is returned
.validNec(Cofree(funcExpr.copy(retValue = Some(re.value)), tree.tail)) Validated.invalidNec(
FuncReturnError[F](
case _ => re.token.unit,
Validated.invalidNec( "Trying to return a value from function that has no return type. Please add return type to function declaration, e.g. `func foo() -> RetType:`"
FuncReturnError[F](
ret.unit,
"Return type is defined for function, but nothing returned. Use `<- value` as the last expression inside function body."
)
) )
)
)
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) case _ => Validated.validNec(tree)

View File

@ -1,16 +1,17 @@
package aqua.parser.expr package aqua.parser.expr
import 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.lexer.Value
import aqua.parser.lift.LiftParser import aqua.parser.lift.LiftParser
import cats.Comonad import cats.Comonad
import cats.data.NonEmptyList
import cats.parse.Parser 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 { object ReturnExpr extends Expr.Leaf {
override def p[F[_]: LiftParser: Comonad]: Parser[ReturnExpr[F]] = override def p[F[_]: LiftParser: Comonad]: Parser[ReturnExpr[F]] =
(`<-` *> ` ` *> Value.`value`[F]).map(ReturnExpr(_)) (`<-` *> ` ` *> comma(Value.`value`[F])).map(ReturnExpr(_))
} }

View File

@ -72,38 +72,32 @@ object BasicTypeToken {
.map(BasicTypeToken(_)) .map(BasicTypeToken(_))
} }
sealed trait ArrowDef[F[_]] {
def argTypes: List[TypeToken[F]]
def resType: Option[DataTypeToken[F]]
}
case class ArrowTypeToken[F[_]: Comonad]( case class ArrowTypeToken[F[_]: Comonad](
override val unit: F[Unit], override val unit: F[Unit],
args: List[DataTypeToken[F]], args: List[(Option[Name[F]], TypeToken[F])],
res: Option[DataTypeToken[F]] res: List[DataTypeToken[F]]
) extends TypeToken[F] with ArrowDef[F] { ) extends TypeToken[F] {
override def as[T](v: T): F[T] = unit.as(v) override def as[T](v: T): F[T] = unit.as(v)
override def argTypes: List[TypeToken[F]] = args def argTypes: List[TypeToken[F]] = args.map(_._2)
override def resType: Option[DataTypeToken[F]] = res
} }
object ArrowTypeToken { object ArrowTypeToken {
def `arrowdef`[F[_]: LiftParser: Comonad]: P[ArrowTypeToken[F]] = def `arrowdef`[F[_]: LiftParser: Comonad](argTypeP: P[TypeToken[F]]): P[ArrowTypeToken[F]] =
(comma0(DataTypeToken.`datatypedef`).with1 ~ ` -> `.lift ~ (comma0(argTypeP).with1 ~ ` -> `.lift ~
(DataTypeToken.`datatypedef` (comma(DataTypeToken.`datatypedef`).map(_.toList)
.map(Some(_)) | `()`.as(None))).map { case ((args, point), res) | `()`.as(Nil))).map { case ((args, point), res)
ArrowTypeToken(point, args, 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( (((`(`.lift <* `/s*`) ~ comma0(
(Name.p[F] *> ` : ` *> DataTypeToken.`datatypedef`).surroundedBy(`/s*`) (Name.p[F].map(Option(_)) ~ (` : ` *> (argTypeP | argTypeP.between(`(`, `)`))))
.surroundedBy(`/s*`)
) <* (`/s*` *> `)`)) ~ ) <* (`/s*` *> `)`)) ~
(` -> ` *> DataTypeToken.`datatypedef`).?).map { case ((point, args), res) => (` -> ` *> comma(DataTypeToken.`datatypedef`)).?).map { case ((point, args), res) =>
ArrowTypeToken(point, args, res) ArrowTypeToken(point, args, res.toList.flatMap(_.toList))
} }
} }
@ -130,7 +124,9 @@ object TypeToken {
def `typedef`[F[_]: LiftParser: Comonad]: P[TypeToken[F]] = def `typedef`[F[_]: LiftParser: Comonad]: P[TypeToken[F]] =
P.oneOf( P.oneOf(
ArrowTypeToken.`arrowdef`.backtrack :: DataTypeToken.`datatypedef` :: Nil ArrowTypeToken
.`arrowdef`((DataTypeToken.`datatypedef`[F]))
.backtrack :: DataTypeToken.`datatypedef` :: Nil
) )
} }

View File

@ -39,7 +39,13 @@ object AquaSpec {
args: List[DataTypeToken[Id]], args: List[DataTypeToken[Id]],
res: Option[DataTypeToken[Id]] res: Option[DataTypeToken[Id]]
): ArrowTypeToken[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] = implicit def toCustomArg(str: String, customType: String): Arg[Id] =
Arg[Id](str, toCustomType(customType)) Arg[Id](str, toCustomType(customType))

View File

@ -2,6 +2,7 @@ package aqua.parser
import aqua.AquaSpec import aqua.AquaSpec
import aqua.parser.expr.ArrowTypeExpr import aqua.parser.expr.ArrowTypeExpr
import aqua.parser.lexer.ArrowTypeToken
import aqua.types.ScalarType.{string, u32} import aqua.types.ScalarType.{string, u32}
import cats.Id import cats.Id
import org.scalatest.flatspec.AnyFlatSpec 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( 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( 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")) 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))
)
)
)
} }
} }

View File

@ -11,10 +11,10 @@ class CallArrowSpec extends AnyFlatSpec with Matchers with AquaSpec {
import AquaSpec._ import AquaSpec._
"func calls" should "parse func()" in { "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( parseExpr("Ab.func(arg)") should be(
CallArrowExpr[Id]( CallArrowExpr[Id](
None, Nil,
Some(toAb("Ab")), Some(toAb("Ab")),
Name[Id]("func"), Name[Id]("func"),
List(VarLambda[Id](toName("arg"))) List(VarLambda[Id](toName("arg")))
@ -23,7 +23,7 @@ class CallArrowSpec extends AnyFlatSpec with Matchers with AquaSpec {
parseExpr("func(arg.doSomething)") should be( parseExpr("func(arg.doSomething)") should be(
CallArrowExpr[Id]( CallArrowExpr[Id](
None, Nil,
None, None,
Name[Id]("func"), Name[Id]("func"),
List(toVarLambda("arg", List("doSomething"))) 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( parseExpr("func(arg.doSomething.and.doSomethingElse)") should be(
CallArrowExpr[Id]( CallArrowExpr[Id](
None, Nil,
None, None,
Name[Id]("func"), Name[Id]("func"),
List(toVarLambda("arg", List("doSomething", "and", "doSomethingElse"))) 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( parseExpr("func(arg.doSomething.and.doSomethingElse)") should be(
CallArrowExpr[Id]( CallArrowExpr[Id](
None, Nil,
None, None,
Name[Id]("func"), Name[Id]("func"),
List(toVarLambda("arg", List("doSomething", "and", "doSomethingElse"))) 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( parseExpr("Ab.func(arg.doSomething.and.doSomethingElse, arg2.someFunc)") should be(
CallArrowExpr[Id]( CallArrowExpr[Id](
None, Nil,
Some(toAb("Ab")), Some(toAb("Ab")),
Name[Id]("func"), Name[Id]("func"),
List( List(
@ -62,7 +62,18 @@ class CallArrowSpec extends AnyFlatSpec with Matchers with AquaSpec {
parseExpr("x <- func(arg.doSomething)") should be( parseExpr("x <- func(arg.doSomething)") should be(
CallArrowExpr[Id]( 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, None,
Name[Id]("func"), Name[Id]("func"),
List( List(

View File

@ -20,7 +20,7 @@ class CoExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
Chain( Chain(
Cofree[Chain, Expr[Id]]( Cofree[Chain, Expr[Id]](
CallArrowExpr( CallArrowExpr(
Some(AquaSpec.toName("x")), List(AquaSpec.toName("x")),
None, None,
AquaSpec.toName("y"), AquaSpec.toName("y"),
Nil Nil

View File

@ -7,7 +7,7 @@ import aqua.parser.lexer.{ArrowTypeToken, BasicTypeToken, EqOp, Literal, Token,
import aqua.parser.lift.LiftParser.Implicits.idLiftParser import aqua.parser.lift.LiftParser.Implicits.idLiftParser
import aqua.types.ScalarType.* import aqua.types.ScalarType.*
import cats.Id import cats.Id
import cats.data.Chain import cats.data.{Chain, NonEmptyList}
import cats.free.Cofree import cats.free.Cofree
import cats.syntax.foldable.* import cats.syntax.foldable.*
import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.flatspec.AnyFlatSpec
@ -21,43 +21,43 @@ class FuncExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
"func header" should "parse" in { "func header" should "parse" in {
funcExpr("func some() -> bool") should be( 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 = 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("func some(peer: PeerId, other: u8 -> bool)") should be(
FuncExpr( FuncExpr(
toName("some"), toName("some"),
List(toCustomArg("peer", "PeerId"), toArg("other", arrowToken)), toNamedArrow(("peer" -> toCustomType("PeerId")) :: ("other" -> arrowToken) :: Nil, Nil),
None, Nil
None
) )
) )
val arrowToken2 = val arrowToken2 =
ArrowTypeToken[Id]( ArrowTypeToken[Id](
(), (),
List(BasicTypeToken[Id](u32), BasicTypeToken[Id](u64)), List(None -> BasicTypeToken[Id](u32), None -> BasicTypeToken[Id](u64)),
Some(BasicTypeToken[Id](bool)) List(BasicTypeToken[Id](bool))
) )
funcExpr("func some(peer: PeerId, other: u32, u64 -> bool)") should be( funcExpr("func some(peer: PeerId, other: u32, u64 -> bool)") should be(
FuncExpr( FuncExpr(
toName("some"), toName("some"),
List(toCustomArg("peer", "PeerId"), toArg("other", arrowToken2)), toNamedArrow(("peer" -> toCustomType("PeerId")) :: ("other" -> arrowToken2) :: Nil, Nil),
None, Nil
None
) )
) )
val arrowToken3 = ArrowTypeToken[Id]((), List(BasicTypeToken[Id](u32)), None) val arrowToken3 = ArrowTypeToken[Id]((), List(None -> BasicTypeToken[Id](u32)), Nil)
funcExpr("func getTime(peer: PeerId, ret: u32 -> ()) -> string") should be( funcExpr("func getTime(peer: PeerId, ret: u32 -> ()) -> string, u32") should be(
FuncExpr( FuncExpr(
toName("getTime"), toName("getTime"),
List(toCustomArg("peer", "PeerId"), toArg("ret", arrowToken3)), toNamedArrow(
Some(BasicTypeToken[Id](string)), ("peer" -> toCustomType("PeerId")) :: ("ret" -> arrowToken3) :: Nil,
None BasicTypeToken[Id](string) :: BasicTypeToken[Id](u32) :: Nil
),
Nil
) )
) )
} }
@ -88,7 +88,7 @@ class FuncExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
| call(true)""".stripMargin | call(true)""".stripMargin
val tree = FuncExpr.ast[Id]().parseAll(script).value.toEither.value 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 = val ifBody =
checkHeadGetTail( checkHeadGetTail(
@ -98,10 +98,10 @@ class FuncExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
).toList ).toList
ifBody.head.head should be( 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(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 // Local service
qTree.d() shouldBe ServiceExpr(toAb("Local"), Some(toStr("local"))) qTree.d() shouldBe ServiceExpr(toAb("Local"), Some(toStr("local")))
qTree.d() shouldBe ArrowTypeExpr("gt", toArrowType(Nil, Some(scToBt(bool)))) 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 OnExpr(toStr("deeper"), List(toStr("deep")))
qTree.d() shouldBe CallArrowExpr(Some("v"), Some(toAb("Local")), "gt", Nil) qTree.d() shouldBe CallArrowExpr(List("v"), Some(toAb("Local")), "gt", Nil)
qTree.d() shouldBe ReturnExpr(toVar("v")) qTree.d() shouldBe ReturnExpr(NonEmptyList.one(toVar("v")))
// genC function // genC function
qTree.d() shouldBe FuncExpr( qTree.d() shouldBe FuncExpr(
"genC", "genC",
List(toArgSc("val", string)), toNamedArrow(("val" -> string) :: Nil, boolSc :: Nil),
Some(boolSc), List("two": VarLambda[Id])
Some("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 OnExpr(toStr("smth"), List(toStr("else")))
qTree.d() shouldBe CallArrowExpr(Some("two"), None, "tryGen", List()) qTree.d() shouldBe CallArrowExpr(List("two"), None, "tryGen", List())
qTree.d() shouldBe CallArrowExpr(Some("three"), Some(toAb("Local")), "gt", List()) qTree.d() shouldBe CallArrowExpr(List("three"), Some(toAb("Local")), "gt", List())
qTree.d() shouldBe ReturnExpr(toVar("two")) qTree.d() shouldBe ReturnExpr(NonEmptyList.one(toVar("two")))
/* TODO this is semantics, not parser test /* TODO this is semantics, not parser test
val f = val f =

View File

@ -20,7 +20,7 @@ class ParExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
Chain( Chain(
Cofree[Chain, Expr[Id]]( Cofree[Chain, Expr[Id]](
CallArrowExpr( CallArrowExpr(
Some(AquaSpec.toName("x")), List(AquaSpec.toName("x")),
None, None,
AquaSpec.toName("y"), AquaSpec.toName("y"),
Nil Nil

View File

@ -3,6 +3,7 @@ package aqua.parser
import aqua.AquaSpec import aqua.AquaSpec
import aqua.parser.expr.ReturnExpr import aqua.parser.expr.ReturnExpr
import cats.Id import cats.Id
import cats.data.NonEmptyList
import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
@ -11,7 +12,7 @@ class ReturnExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
"return" should "be parsed" in { "return" should "be parsed" in {
parseReturn("<- true") should be( parseReturn("<- true") should be(
ReturnExpr[Id](toBool(true)) ReturnExpr[Id](NonEmptyList.one(toBool(true)))
) )
} }
} }

View File

@ -20,38 +20,56 @@ class TypeTokenSpec extends AnyFlatSpec with Matchers with EitherValues {
} }
"Arrow type" should "parse" in { "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( arrowdef.parseAll("-> B").value should be(
ArrowTypeToken[Id]((), Nil, Some(CustomTypeToken[Id]("B"))) ArrowTypeToken[Id]((), Nil, List(CustomTypeToken[Id]("B")))
) )
ArrowTypeToken.`arrowdef`.parseAll("A -> B").value should be( 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(
ArrowTypeToken[Id]( ArrowTypeToken[Id](
(), (),
CustomTypeToken[Id]("A") :: (u32: BasicTypeToken[Id]) :: Nil, (None -> CustomTypeToken[Id]("A")) :: Nil,
Some(CustomTypeToken[Id]("B")) List(CustomTypeToken[Id]("B"))
) )
) )
ArrowTypeToken.`arrowdef`.parseAll("[]Absolutely, u32 -> B").value should be(
arrowWithNames.parseAll("(a: A) -> B").value should be(
ArrowTypeToken[Id]( 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 Id
]) :: Nil, ])) :: Nil,
Some(CustomTypeToken[Id]("B")) CustomTypeToken[Id]("B") ::
CustomTypeToken[Id]("C") :: Nil
) )
) )

View File

@ -29,21 +29,24 @@ class CallArrowSem[F[_]](val expr: CallArrowExpr[F]) extends AnyVal {
N: NamesAlgebra[F, Alg], N: NamesAlgebra[F, Alg],
T: TypesAlgebra[F, Alg], T: TypesAlgebra[F, Alg],
V: ValuesAlgebra[F, Alg] V: ValuesAlgebra[F, Alg]
): Free[Alg, (List[ValueModel], Option[Type])] = ): Free[Alg, (List[ValueModel], List[Type])] =
V.checkArguments(expr.funcName, at, args) >> variable V.checkArguments(expr.funcName, at, args) >> variables
.fold(freeUnit[Alg].as(Option.empty[Type]))(exportVar => .foldLeft(freeUnit[Alg].as((List.empty[Type], at.codomain.toList)))((f, exportVar) =>
at.res.fold( f.flatMap {
// TODO: error! we're trying to export variable, but function has no export type case (exports, Nil) =>
freeUnit[Alg].as(Option.empty[Type]) freeUnit[Alg].as(exports -> Nil)
)(resType =>
N.read(exportVar, mustBeDefined = false).flatMap { case (exports, resType :: codom) =>
case Some(t @ StreamType(st)) => N.read(exportVar, mustBeDefined = false).flatMap {
T.ensureTypeMatches(exportVar, st, resType).as(Option(t)) case Some(t @ StreamType(st)) =>
case _ => N.define(exportVar, resType).as(at.res) T.ensureTypeMatches(exportVar, st, resType).as((t :: exports, codom))
} case _ =>
) N.define(exportVar, resType).as((resType :: exports, codom))
) >>= { (v: Option[Type]) => }
Traverse[List].traverse(args)(V.valueToModel).map(_.flatten).map(_ -> v) }
)
.map(_._1) >>= { (v: List[Type]) =>
Traverse[List].traverse(args)(V.valueToModel).map(_.flatten -> v.reverse)
} }
private def toModel[Alg[_]](implicit private def toModel[Alg[_]](implicit
@ -74,30 +77,30 @@ 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]
): Free[Alg, FuncOp] = { ): Free[Alg, FuncOp] = {
checkArgsRes(arrowType).flatMap { case (argsResolved, tOp) => checkArgsRes(arrowType).flatMap { (argsResolved, resTypes) =>
((variable, tOp) match { variables
case (Some(v), Some(t)) => .drop(arrowType.codomain.length)
Free.pure[Alg, Option[Call.Export]](Option(Call.Export(v.value, t))) .headOption
case (Some(v), None) => .fold(
T.expectNoExport(v).map(_ => None) Free.pure((variables zip resTypes).map { case (v, t) =>
case _ => Call.Export(v.value, t)
Free.pure[Alg, Option[Call.Export]](None) })
)(T.expectNoExport(_).as(Nil))
}).map(maybeExport => .map(maybeExport =>
FuncOp.leaf(serviceId match { FuncOp.leaf(serviceId match {
case Some(sid) => case Some(sid) =>
CallServiceTag( CallServiceTag(
serviceId = sid, serviceId = sid,
funcName = funcName.value, funcName = funcName.value,
Call(argsResolved, maybeExport.toList) Call(argsResolved, maybeExport)
) )
case None => case None =>
CallArrowTag( CallArrowTag(
funcName = funcName.value, funcName = funcName.value,
Call(argsResolved, maybeExport.toList) Call(argsResolved, maybeExport)
) )
}) })
) )
} }
} }

View File

@ -29,35 +29,40 @@ class FuncSem[F[_]](val expr: FuncExpr[F]) extends AnyVal {
A.beginScope(name) >> Applicative[Free[Alg, *]] A.beginScope(name) >> Applicative[Free[Alg, *]]
.product( .product(
// Collect argument types, define local variables // Collect argument types, define local variables
args arrowTypeExpr.args
.foldLeft( .foldLeft(
// Begin scope -- for mangling // Begin scope -- for mangling
N.beginScope(name).as[Chain[(String, Type)]](Chain.empty) N.beginScope(name).as[Chain[(String, Type)]](Chain.empty)
) { case (f, Arg(argName, argType)) => ) {
// Resolve arg type, remember it case (f, (Some(argName), argType)) =>
f.flatMap(acc => // Resolve arg type, remember it
T.resolveType(argType).flatMap { f.flatMap(acc =>
case Some(t: ArrowType) => T.resolveType(argType).flatMap {
N.defineArrow(argName, t, isRoot = false).as(acc.append(argName.value -> t)) case Some(t: ArrowType) =>
case Some(t) => N.defineArrow(argName, t, isRoot = false).as(acc.append(argName.value -> t))
N.define(argName, t).as(acc.append(argName.value -> t)) case Some(t) =>
case None => N.define(argName, t).as(acc.append(argName.value -> t))
Free.pure(acc) case None =>
} Free.pure(acc)
) }
)
// Unnamed argument
case (f, _) => f
} }
.map(_.toList), .map(_.toList),
// Resolve return type // 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 => .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)( def generateFuncModel[Alg[_]](funcArrow: ArrowType, retModel: List[ValueModel], body: FuncOp)(
implicit N: NamesAlgebra[F, Alg] implicit N: NamesAlgebra[F, Alg]
): Free[Alg, Model] = { ): Free[Alg, Model] = {
val argNames = args.map(_.name.value) val argNames = arrowTypeExpr.args.collect { case (Some(n), _) => n.value }
val model = FuncModel( val model = FuncModel(
name = name.value, name = name.value,
@ -80,27 +85,27 @@ class FuncSem[F[_]](val expr: FuncExpr[F]) extends AnyVal {
A: AbilitiesAlgebra[F, Alg] A: AbilitiesAlgebra[F, Alg]
): Free[Alg, Model] = ): Free[Alg, Model] =
// Check return value type // Check return value type
((funcArrow.res, retValue) match { ((funcArrow.codomain.toList zip retValue)
case (Some(t), Some(v)) => .foldLeft(Free.pure[Alg, List[ValueModel]](Nil)) { case (f, (t, v)) =>
V.valueToModel(v) f.flatMap(vs =>
.flatTap { V.valueToModel(v)
case Some(vt) => T.ensureTypeMatches(v, t, vt.lastType).void .flatTap {
case None => Free.pure[Alg, Unit](()) case Some(vt) => T.ensureTypeMatches(v, t, vt.lastType).void
} case None => Free.pure[Alg, Unit](())
.map(_.toList) }
case (_, _) => .map(vs.prependedAll)
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))
}) })
) .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 def program[Alg[_]](implicit
T: TypesAlgebra[F, Alg], T: TypesAlgebra[F, Alg],

View File

@ -5,9 +5,10 @@ import aqua.parser.expr.ReturnExpr
import aqua.semantics.Prog import aqua.semantics.Prog
import aqua.semantics.rules.ValuesAlgebra import aqua.semantics.rules.ValuesAlgebra
import cats.syntax.functor._ import cats.syntax.functor._
import cats.syntax.traverse._
class ReturnSem[F[_]](val expr: ReturnExpr[F]) extends AnyVal { class ReturnSem[F[_]](val expr: ReturnExpr[F]) extends AnyVal {
def program[Alg[_]](implicit V: ValuesAlgebra[F, Alg]): Prog[Alg, Model] = 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)
} }

View File

@ -59,23 +59,33 @@ case class TypesState[F[_]](
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) =>
val strictArgs = args.map(resolveTypeToken).collect { case Some(dt: DataType) => val strictArgs = args.map(_._2).map(resolveTypeToken).collect { case Some(dt: DataType) =>
dt dt
} }
val strictRes = res.flatMap(resolveTypeToken).collect { case dt: DataType => val strictRes: List[DataType] = res.flatMap(resolveTypeToken).collect { case dt: DataType =>
dt 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)) ArrowType(ProductType(strictArgs), ProductType(strictRes.toList))
) )
} }
def resolveArrowDef(ad: ArrowTypeToken[F]): ValidatedNec[(Token[F], String), ArrowType] = def resolveArrowDef(ad: ArrowTypeToken[F]): ValidatedNec[(Token[F], String), ArrowType] = {
ad.resType.flatMap(resolveTypeToken) match { val resType = ad.res.map(resolveTypeToken)
case resType if resType.isDefined == ad.resType.isDefined =>
val (errs, argTypes) = ad.argTypes NonEmptyChain
.map(tt => resolveTypeToken(tt).toRight(tt -> s"Type unresolved")) .fromChain(Chain.fromSeq(ad.res.zip(resType).collect { case (dt, None) =>
.foldLeft[(Chain[(Token[F], String)], Chain[Type])]((Chain.empty, Chain.empty)) { 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), Right(at)) => (errs, argTypes.append(at))
case ((errs, argTypes), Left(e)) => (errs.append(e), argTypes) case ((errs, argTypes), Left(e)) => (errs.append(e), argTypes)
} }
@ -83,12 +93,15 @@ case class TypesState[F[_]](
NonEmptyChain NonEmptyChain
.fromChain(errs) .fromChain(errs)
.fold[ValidatedNec[(Token[F], String), ArrowType]]( .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(_)) )(Invalid(_))
}(Invalid(_))
case _ => }
Invalid(NonEmptyChain.one(ad.resType.getOrElse(ad) -> "Cannot resolve the result type"))
}
def resolveOps( def resolveOps(
rootT: Type, rootT: Type,

View File

@ -70,7 +70,7 @@ object CompareTypes {
) -1.0 ) -1.0
else NaN 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 (NilType, NilType) => 0.0
case (_: ConsType, NilType) => -1.0 case (_: ConsType, NilType) => -1.0
case (NilType, _: ConsType) => 1.0 case (NilType, _: ConsType) => 1.0

View File

@ -59,6 +59,14 @@ object ProductType {
case _ => NilType 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 { def labelled(types: List[(String, Type)]): ProductType = types match {
case (l, h) :: t => case (l, h) :: t =>
ConsType.cons(l, h, ProductType.labelled(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 { case class ArrowType(domain: ProductType, codomain: ProductType) extends Type {
@deprecated( lazy val res: Option[Type] = codomain.toList match {
"Use .domain to get arguments, add .args helper to the typed object, if needed", case Nil => None
"5.08.2021" case a :: Nil => Some(a)
) case _ => Some(codomain)
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))
override def toString: String = override def toString: String =
s"$domain -> $codomain" s"$domain -> $codomain"