mirror of
https://github.com/fluencelabs/aqua.git
synced 2024-12-04 22:50:18 +00:00
fix(compiler): Fix nested abilities [fixes LNG-220] (#852)
* Fix fields gathering * Remove println * Add test * Remove println * Add comments * Add comments
This commit is contained in:
parent
5db1282c1f
commit
bf0b51fa5b
@ -5,11 +5,13 @@ import aqua.model.*
|
|||||||
import aqua.model.inline.state.{Arrows, Exports, Mangler}
|
import aqua.model.inline.state.{Arrows, Exports, Mangler}
|
||||||
import aqua.raw.ops.RawTag
|
import aqua.raw.ops.RawTag
|
||||||
import aqua.raw.value.{ValueRaw, VarRaw}
|
import aqua.raw.value.{ValueRaw, VarRaw}
|
||||||
import aqua.types.{AbilityType, ArrowType, BoxType, StreamType}
|
import aqua.types.{AbilityType, ArrowType, BoxType, StreamType, Type}
|
||||||
|
|
||||||
import cats.data.{Chain, IndexedStateT, State}
|
import cats.data.{Chain, IndexedStateT, State}
|
||||||
import cats.syntax.bifunctor.*
|
import cats.syntax.bifunctor.*
|
||||||
import cats.syntax.foldable.*
|
import cats.syntax.foldable.*
|
||||||
import cats.syntax.traverse.*
|
import cats.syntax.traverse.*
|
||||||
|
import cats.syntax.option.*
|
||||||
import cats.{Eval, Monoid}
|
import cats.{Eval, Monoid}
|
||||||
import scribe.Logging
|
import scribe.Logging
|
||||||
|
|
||||||
@ -117,24 +119,23 @@ object ArrowInliner extends Logging {
|
|||||||
exports <- Exports[S].exports
|
exports <- Exports[S].exports
|
||||||
arrows <- Arrows[S].arrows
|
arrows <- Arrows[S].arrows
|
||||||
// gather all arrows and variables from abilities
|
// gather all arrows and variables from abilities
|
||||||
returnedFromAbilities = rets.collect { case VarModel(name, st @ AbilityType(_, _), _) =>
|
returnedAbilities = rets.collect { case VarModel(name, at: AbilityType, _) => name -> at }
|
||||||
getVarsAndArrowsFromAbilities(name, None, st, exports, arrows)
|
varsFromAbilities = returnedAbilities.flatMap { case (name, at) =>
|
||||||
}.foldMapA(_.bimap(_.toList, _.toList)).bimap(_.toMap, _.toMap)
|
getAbilityVars(name, None, at, exports)
|
||||||
|
}.toMap
|
||||||
|
arrowsFromAbilities = returnedAbilities.flatMap { case (name, at) =>
|
||||||
|
getAbilityArrows(name, None, at, exports, arrows)
|
||||||
|
}.toMap
|
||||||
|
|
||||||
// find and get resolved arrows if we return them from the function
|
// find and get resolved arrows if we return them from the function
|
||||||
returnedArrows = rets.collect { case VarModel(name, ArrowType(_, _), _) =>
|
returnedArrows = rets.collect { case VarModel(name, _: ArrowType, _) => name }.toSet
|
||||||
name
|
|
||||||
}.toSet
|
|
||||||
arrowsToSave <- Arrows[S].pickArrows(returnedArrows)
|
arrowsToSave <- Arrows[S].pickArrows(returnedArrows)
|
||||||
} yield {
|
} yield InlineResult(
|
||||||
val (valsFromAbilities, arrowsFromAbilities) = returnedFromAbilities
|
SeqModel.wrap(ops.reverse),
|
||||||
InlineResult(
|
rets.reverse,
|
||||||
SeqModel.wrap(ops.reverse: _*),
|
varsFromAbilities,
|
||||||
rets.reverse,
|
arrowsFromAbilities ++ arrowsToSave
|
||||||
valsFromAbilities,
|
)
|
||||||
arrowsFromAbilities ++ arrowsToSave
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all arrows that is arguments from outer Arrows.
|
* Get all arrows that is arguments from outer Arrows.
|
||||||
@ -271,75 +272,108 @@ object ArrowInliner extends Logging {
|
|||||||
AbilityType.fullName(name, n) -> AbilityType.fullName(newName, n)
|
AbilityType.fullName(name, n) -> AbilityType.fullName(newName, n)
|
||||||
}
|
}
|
||||||
allNewNames = newFieldsName.add((name, newName)).toSortedMap
|
allNewNames = newFieldsName.add((name, newName)).toSortedMap
|
||||||
} yield {
|
allVars = getAbilityVars(vm.name, newName.some, t, exports)
|
||||||
val (allVars, allArrows) =
|
allArrows = getAbilityArrows(vm.name, newName.some, t, exports, arrows)
|
||||||
getVarsAndArrowsFromAbilities(vm.name, Option(newName), t, exports, arrows)
|
} yield AbilityResolvingResult(allNewNames, allVars, allArrows)
|
||||||
AbilityResolvingResult(allNewNames, allVars, allArrows)
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get ability fields (vars or arrows) from exports
|
||||||
|
*
|
||||||
|
* @param abilityName ability current name in state
|
||||||
|
* @param abilityNewName ability new name (for renaming)
|
||||||
|
* @param abilityType ability type
|
||||||
|
* @param exports exports state to resolve fields
|
||||||
|
* @param fields fields selector
|
||||||
|
* @return resolved ability fields (renamed if necessary)
|
||||||
|
*/
|
||||||
|
private def getAbilityFields[T <: Type](
|
||||||
|
abilityName: String,
|
||||||
|
abilityNewName: Option[String],
|
||||||
|
abilityType: AbilityType,
|
||||||
|
exports: Map[String, ValueModel]
|
||||||
|
)(fields: AbilityType => Map[String, T]): Map[String, ValueModel] =
|
||||||
|
fields(abilityType).flatMap { case (fName, _) =>
|
||||||
|
val fullName = AbilityType.fullName(abilityName, fName)
|
||||||
|
val newFullName = AbilityType.fullName(abilityNewName.getOrElse(abilityName), fName)
|
||||||
|
|
||||||
|
Exports
|
||||||
|
.getLastValue(fullName, exports)
|
||||||
|
.map(newFullName -> _)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get ability vars and arrows as vars from exports
|
||||||
|
*
|
||||||
|
* @param abilityName ability current name in state
|
||||||
|
* @param abilityNewName ability new name (for renaming)
|
||||||
|
* @param abilityType ability type
|
||||||
|
* @param exports exports state to resolve fields
|
||||||
|
* @return resolved ability vars and arrows as vars (renamed if necessary)
|
||||||
|
*/
|
||||||
|
private def getAbilityVars(
|
||||||
|
abilityName: String,
|
||||||
|
abilityNewName: Option[String],
|
||||||
|
abilityType: AbilityType,
|
||||||
|
exports: Map[String, ValueModel]
|
||||||
|
): Map[String, ValueModel] = {
|
||||||
|
val get = getAbilityFields(
|
||||||
|
abilityName,
|
||||||
|
abilityNewName,
|
||||||
|
abilityType,
|
||||||
|
exports
|
||||||
|
)
|
||||||
|
|
||||||
|
get(_.variables) ++ get(_.arrows).flatMap {
|
||||||
|
case arrow @ (_, vm: VarModel) =>
|
||||||
|
arrow.some
|
||||||
|
case (_, m) =>
|
||||||
|
logger.error(s"Unexpected: '$m' cannot be an arrow")
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gather all arrows and variables from abilities recursively (because of possible nested abilities).
|
* Get ability arrows from arrows
|
||||||
* Rename top names if needed in gathered fields and arrows.
|
*
|
||||||
* `top` name is a first name, i.e.: `topName.fieldName`.
|
* @param abilityName ability current name in state
|
||||||
* Only top name must be renamed to keep all field names unique.
|
* @param abilityNewName ability new name (for renaming)
|
||||||
* @param topOldName old name to find all fields in states
|
* @param abilityType ability type
|
||||||
* @param topNewName new name to rename all fields in states
|
* @param exports exports state to resolve fields
|
||||||
* @param abilityType type of current ability
|
* @param arrows arrows state to resolve arrows
|
||||||
* @param exports where to get values
|
* @return resolved ability arrows (renamed if necessary)
|
||||||
* @param arrows where to get arrows
|
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
private def getVarsAndArrowsFromAbilities(
|
private def getAbilityArrows(
|
||||||
topOldName: String,
|
abilityName: String,
|
||||||
topNewName: Option[String],
|
abilityNewName: Option[String],
|
||||||
abilityType: AbilityType,
|
abilityType: AbilityType,
|
||||||
exports: Map[String, ValueModel],
|
exports: Map[String, ValueModel],
|
||||||
arrows: Map[String, FuncArrow]
|
arrows: Map[String, FuncArrow]
|
||||||
): (Map[String, ValueModel], Map[String, FuncArrow]) = {
|
): Map[String, FuncArrow] = {
|
||||||
abilityType.fields.toSortedMap.toList.map { case (fName, fValue) =>
|
val get = getAbilityFields(
|
||||||
val currentOldName = AbilityType.fullName(topOldName, fName)
|
abilityName,
|
||||||
// for all nested fields, arrows and abilities only left side must be renamed
|
abilityNewName,
|
||||||
val currentNewName = topNewName.map(AbilityType.fullName(_, fName))
|
abilityType,
|
||||||
fValue match {
|
exports
|
||||||
case nestedAbilityType @ AbilityType(_, _) =>
|
)
|
||||||
getVarsAndArrowsFromAbilities(
|
|
||||||
currentOldName,
|
|
||||||
currentNewName,
|
|
||||||
nestedAbilityType,
|
|
||||||
exports,
|
|
||||||
arrows
|
|
||||||
)
|
|
||||||
case ArrowType(_, _) =>
|
|
||||||
Exports
|
|
||||||
.getLastValue(currentOldName, exports)
|
|
||||||
.flatMap {
|
|
||||||
case vm @ VarModel(name, _, _) =>
|
|
||||||
arrows
|
|
||||||
.get(name)
|
|
||||||
.map(fa =>
|
|
||||||
(
|
|
||||||
Map(currentNewName.getOrElse(currentOldName) -> vm),
|
|
||||||
Map(name -> fa)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
case lm @ LiteralModel(_, _) =>
|
|
||||||
logger.error(s"Unexpected. Literal '$lm' cannot be an arrow")
|
|
||||||
None
|
|
||||||
}
|
|
||||||
.getOrElse((Map.empty, Map.empty))
|
|
||||||
|
|
||||||
case _ =>
|
get(_.arrows).flatMap {
|
||||||
Exports
|
case (_, VarModel(name, _, _)) =>
|
||||||
.getLastValue(currentOldName, exports)
|
arrows.get(name).map(name -> _)
|
||||||
.map { vm =>
|
case (_, m) =>
|
||||||
(Map(currentNewName.getOrElse(currentOldName) -> vm), Map.empty)
|
logger.error(s"Unexpected: '$m' cannot be an arrow")
|
||||||
}
|
None
|
||||||
.getOrElse((Map.empty, Map.empty))
|
}
|
||||||
}
|
|
||||||
}.foldMapA(_.bimap(_.toList, _.toList)).bimap(_.toMap, _.toMap)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def getAbilityArrows[S: Arrows: Exports](
|
||||||
|
abilityName: String,
|
||||||
|
abilityType: AbilityType
|
||||||
|
): State[S, Map[String, FuncArrow]] = for {
|
||||||
|
exports <- Exports[S].exports
|
||||||
|
arrows <- Arrows[S].arrows
|
||||||
|
} yield getAbilityArrows(abilityName, None, abilityType, exports, arrows)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare the state context for this function call
|
* Prepare the state context for this function call
|
||||||
*
|
*
|
||||||
@ -435,25 +469,6 @@ object ArrowInliner extends Logging {
|
|||||||
// Result could be renamed; take care about that
|
// Result could be renamed; take care about that
|
||||||
} yield (tree, fn.ret.map(_.renameVars(shouldRename)))
|
} yield (tree, fn.ret.map(_.renameVars(shouldRename)))
|
||||||
|
|
||||||
private def getAllArrowsFromAbility[S: Exports: Arrows: Mangler](
|
|
||||||
name: String,
|
|
||||||
sc: AbilityType
|
|
||||||
): State[S, Map[String, FuncArrow]] = {
|
|
||||||
for {
|
|
||||||
exports <- Exports[S].exports
|
|
||||||
arrows <- Arrows[S].arrows
|
|
||||||
} yield {
|
|
||||||
sc.fields.toSortedMap.toList.flatMap {
|
|
||||||
case (n, ArrowType(_, _)) =>
|
|
||||||
exports.get(AbilityType.fullName(name, n)).flatMap {
|
|
||||||
case VarModel(n, _, _) => arrows.get(n).map(n -> _)
|
|
||||||
case _ => None
|
|
||||||
}
|
|
||||||
case _ => None
|
|
||||||
}.toMap
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private[inline] def callArrowRet[S: Exports: Arrows: Mangler](
|
private[inline] def callArrowRet[S: Exports: Arrows: Mangler](
|
||||||
arrow: FuncArrow,
|
arrow: FuncArrow,
|
||||||
call: CallModel
|
call: CallModel
|
||||||
@ -461,8 +476,8 @@ object ArrowInliner extends Logging {
|
|||||||
for {
|
for {
|
||||||
passArrows <- Arrows[S].pickArrows(call.arrowArgNames)
|
passArrows <- Arrows[S].pickArrows(call.arrowArgNames)
|
||||||
arrowsFromAbilities <- call.abilityArgs
|
arrowsFromAbilities <- call.abilityArgs
|
||||||
.traverse(getAllArrowsFromAbility)
|
.traverse(getAbilityArrows.tupled)
|
||||||
.map(_.fold(Map.empty)(_ ++ _))
|
.map(_.flatMap(_.toList).toMap)
|
||||||
|
|
||||||
exports <- Exports[S].exports
|
exports <- Exports[S].exports
|
||||||
streams <- getOutsideStreamNames
|
streams <- getOutsideStreamNames
|
||||||
|
@ -3,7 +3,7 @@ package aqua.model.inline
|
|||||||
import aqua.model.*
|
import aqua.model.*
|
||||||
import aqua.model.inline.state.InliningState
|
import aqua.model.inline.state.InliningState
|
||||||
import aqua.raw.ops.*
|
import aqua.raw.ops.*
|
||||||
import aqua.raw.value.{ApplyPropertyRaw, FunctorRaw, IntoFieldRaw, IntoIndexRaw, LiteralRaw, VarRaw}
|
import aqua.raw.value.*
|
||||||
import aqua.types.*
|
import aqua.types.*
|
||||||
import cats.syntax.show.*
|
import cats.syntax.show.*
|
||||||
import cats.syntax.option.*
|
import cats.syntax.option.*
|
||||||
@ -49,7 +49,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
records: *string
|
records: *string
|
||||||
cb(records)
|
cb(records)
|
||||||
*/
|
*/
|
||||||
"arrow inliner" should "pass stream to callback properly" in {
|
it should "pass stream to callback properly" in {
|
||||||
val streamType = StreamType(ScalarType.string)
|
val streamType = StreamType(ScalarType.string)
|
||||||
val streamVar = VarRaw("records", streamType)
|
val streamVar = VarRaw("records", streamType)
|
||||||
val streamModel = VarModel("records", StreamType(ScalarType.string))
|
val streamModel = VarModel("records", StreamType(ScalarType.string))
|
||||||
@ -141,7 +141,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
records: *string
|
records: *string
|
||||||
cb(records!)
|
cb(records!)
|
||||||
*/
|
*/
|
||||||
ignore /*"arrow inliner"*/ should "pass stream with gate to callback properly" in {
|
ignore /*it*/ should "pass stream with gate to callback properly" in {
|
||||||
val streamType = StreamType(ScalarType.string)
|
val streamType = StreamType(ScalarType.string)
|
||||||
val streamVar = VarRaw("records", streamType)
|
val streamVar = VarRaw("records", streamType)
|
||||||
val streamVarLambda =
|
val streamVarLambda =
|
||||||
@ -242,7 +242,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
append_records(records)
|
append_records(records)
|
||||||
<- records
|
<- records
|
||||||
*/
|
*/
|
||||||
"arrow inliner" should "work with streams as arguments" in {
|
it should "work with streams as arguments" in {
|
||||||
|
|
||||||
val returnType = ArrayType(ArrayType(ScalarType.string))
|
val returnType = ArrayType(ArrayType(ScalarType.string))
|
||||||
val streamType = StreamType(ArrayType(ScalarType.string))
|
val streamType = StreamType(ArrayType(ScalarType.string))
|
||||||
@ -338,7 +338,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
* retval = res1 + res2 + res3
|
* retval = res1 + res2 + res3
|
||||||
* <- retval
|
* <- retval
|
||||||
*/
|
*/
|
||||||
"arrow inliner" should "leave meta after function inlining" in {
|
it should "leave meta after function inlining" in {
|
||||||
val innerName = "inner"
|
val innerName = "inner"
|
||||||
val innerRes = VarRaw("res", ScalarType.u16)
|
val innerRes = VarRaw("res", ScalarType.u16)
|
||||||
|
|
||||||
@ -475,7 +475,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
* retval = inner() + inner() + 37
|
* retval = inner() + inner() + 37
|
||||||
* <- retval
|
* <- retval
|
||||||
*/
|
*/
|
||||||
"arrow inliner" should "omit meta if arrow was completely erased" in {
|
it should "omit meta if arrow was completely erased" in {
|
||||||
val innerName = "inner"
|
val innerName = "inner"
|
||||||
val innerRes = VarRaw("res", ScalarType.u16)
|
val innerRes = VarRaw("res", ScalarType.u16)
|
||||||
val innerRet = "42"
|
val innerRet = "42"
|
||||||
@ -753,7 +753,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
* retval = 37 + c(1) + c(2)
|
* retval = 37 + c(1) + c(2)
|
||||||
* <- retval
|
* <- retval
|
||||||
*/
|
*/
|
||||||
"arrow inliner" should "leave meta after returned closure inlining" in {
|
it should "leave meta after returned closure inlining" in {
|
||||||
val innerName = "inner"
|
val innerName = "inner"
|
||||||
val closureName = "closure"
|
val closureName = "closure"
|
||||||
val outterClosureName = "c"
|
val outterClosureName = "c"
|
||||||
@ -853,7 +853,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
* retval = 37 + c() + c()
|
* retval = 37 + c() + c()
|
||||||
* <- retval
|
* <- retval
|
||||||
*/
|
*/
|
||||||
"arrow inliner" should "omit meta if returned closure was completely erased" in {
|
it should "omit meta if returned closure was completely erased" in {
|
||||||
val innerName = "inner"
|
val innerName = "inner"
|
||||||
val closureName = "closure"
|
val closureName = "closure"
|
||||||
|
|
||||||
@ -1023,7 +1023,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
* retval = 37 + a(1) + b(2) + c{3}
|
* retval = 37 + a(1) + b(2) + c{3}
|
||||||
* <- retval
|
* <- retval
|
||||||
*/
|
*/
|
||||||
"arrow inliner" should "correctly inline renamed closure [bug LNG-193]" in {
|
it should "correctly inline renamed closure [bug LNG-193]" in {
|
||||||
val innerName = "inner"
|
val innerName = "inner"
|
||||||
val closureName = "closure"
|
val closureName = "closure"
|
||||||
val outterClosureName = "c"
|
val outterClosureName = "c"
|
||||||
@ -1151,7 +1151,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
* resT <- accept_closure(closure)
|
* resT <- accept_closure(closure)
|
||||||
* <- resT
|
* <- resT
|
||||||
*/
|
*/
|
||||||
"arrow inliner" should "correctly handle closure as argument [bug LNG-92]" in {
|
it should "correctly handle closure as argument [bug LNG-92]" in {
|
||||||
val acceptName = "accept_closure"
|
val acceptName = "accept_closure"
|
||||||
val closureName = "closure"
|
val closureName = "closure"
|
||||||
val testName = "test"
|
val testName = "test"
|
||||||
@ -1274,7 +1274,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
* method = "method"
|
* method = "method"
|
||||||
* test(method)
|
* test(method)
|
||||||
*/
|
*/
|
||||||
"arrow inliner" should "not rename service call [bug LNG-199]" in {
|
it should "not rename service call [bug LNG-199]" in {
|
||||||
val testName = "test"
|
val testName = "test"
|
||||||
val argMethodName = "method"
|
val argMethodName = "method"
|
||||||
val serviceName = "Test"
|
val serviceName = "Test"
|
||||||
@ -1381,7 +1381,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
v = arg.value
|
v = arg.value
|
||||||
OpHa.identity(v)
|
OpHa.identity(v)
|
||||||
*/
|
*/
|
||||||
"arrow inliner" should "hold lambda" in {
|
it should "hold lambda" in {
|
||||||
val innerName = "inner"
|
val innerName = "inner"
|
||||||
|
|
||||||
// lambda that will be assigned to another variable
|
// lambda that will be assigned to another variable
|
||||||
@ -1485,7 +1485,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
func joinIdxLocal(idx: i16, nodes: []string):
|
func joinIdxLocal(idx: i16, nodes: []string):
|
||||||
join nodes[idx]
|
join nodes[idx]
|
||||||
*/
|
*/
|
||||||
"arrow inliner" should "not rename value in index array lambda" in {
|
it should "not rename value in index array lambda" in {
|
||||||
val innerName = "inner"
|
val innerName = "inner"
|
||||||
|
|
||||||
// lambda that will be assigned to another variable
|
// lambda that will be assigned to another variable
|
||||||
@ -1575,7 +1575,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"arrow inliner" should "rename value in arrow with same name as in for" in {
|
it should "rename value in arrow with same name as in for" in {
|
||||||
val argVar = VarRaw("arg", ScalarType.u32)
|
val argVar = VarRaw("arg", ScalarType.u32)
|
||||||
val iVar = VarRaw("i", ScalarType.string)
|
val iVar = VarRaw("i", ScalarType.string)
|
||||||
val iVar0 = VarRaw("i-0", ScalarType.string)
|
val iVar0 = VarRaw("i-0", ScalarType.string)
|
||||||
@ -1665,7 +1665,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
get() -> string
|
get() -> string
|
||||||
|
|
||||||
func inner() -> string:
|
func inner() -> string:
|
||||||
results <- DTGetter.get_dt()
|
results <- Get.get()
|
||||||
<- results
|
<- results
|
||||||
|
|
||||||
func outer() -> []string:
|
func outer() -> []string:
|
||||||
@ -1673,7 +1673,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
results <- use_name1()
|
results <- use_name1()
|
||||||
<- results
|
<- results
|
||||||
*/
|
*/
|
||||||
"arrow inliner" should "generate result in right order" in {
|
it should "generate result in right order" in {
|
||||||
val innerName = "inner"
|
val innerName = "inner"
|
||||||
val results = VarRaw("results", ScalarType.string)
|
val results = VarRaw("results", ScalarType.string)
|
||||||
val resultsOut = VarRaw("results", StreamType(ScalarType.string))
|
val resultsOut = VarRaw("results", StreamType(ScalarType.string))
|
||||||
@ -1744,4 +1744,210 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
) should be(true)
|
) should be(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ability Inner:
|
||||||
|
* arrow(x: i8) -> bool
|
||||||
|
* field: i8
|
||||||
|
*
|
||||||
|
* ability Outer:
|
||||||
|
* inner: Inner
|
||||||
|
*
|
||||||
|
* func accept{Outer}() -> bool:
|
||||||
|
* res = Outer.inner.arrow(Outer.inner.field)
|
||||||
|
* <- res
|
||||||
|
*
|
||||||
|
* func main() -> bool:
|
||||||
|
* closure = (x: i8) -> bool:
|
||||||
|
* res = x > 0
|
||||||
|
* <- res
|
||||||
|
* inner = Inner(arrow = closure, field = 42)
|
||||||
|
* outer = Outer(inner = inner)
|
||||||
|
* res <- accept{outer}()
|
||||||
|
* <- res
|
||||||
|
*/
|
||||||
|
it should "handle nested abilities" in {
|
||||||
|
val arrowType = ArrowType(
|
||||||
|
ProductType(ScalarType.i8 :: Nil),
|
||||||
|
ProductType(ScalarType.bool :: Nil)
|
||||||
|
)
|
||||||
|
val closureType = arrowType.copy(
|
||||||
|
domain = ProductType.labelled(
|
||||||
|
"x" -> ScalarType.i8 :: Nil
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val resVar = VarRaw("res", ScalarType.bool)
|
||||||
|
val innerType = AbilityType(
|
||||||
|
"Inner",
|
||||||
|
NonEmptyMap.of(
|
||||||
|
"arrow" -> arrowType,
|
||||||
|
"field" -> ScalarType.i8
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val outerType = AbilityType(
|
||||||
|
"Outer",
|
||||||
|
NonEmptyMap.of(
|
||||||
|
"inner" -> innerType
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val acceptBody = SeqTag.wrap(
|
||||||
|
AssignmentTag(
|
||||||
|
ApplyPropertyRaw.fromChain(
|
||||||
|
VarRaw("Outer", outerType),
|
||||||
|
Chain(
|
||||||
|
IntoFieldRaw("inner", innerType),
|
||||||
|
IntoArrowRaw(
|
||||||
|
"arrow",
|
||||||
|
arrowType,
|
||||||
|
List(
|
||||||
|
ApplyPropertyRaw.fromChain(
|
||||||
|
VarRaw("Outer", outerType),
|
||||||
|
Chain(
|
||||||
|
IntoFieldRaw("inner", innerType),
|
||||||
|
IntoFieldRaw("field", ScalarType.i8)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
resVar.name
|
||||||
|
).leaf,
|
||||||
|
ReturnTag(NonEmptyList.one(resVar)).leaf
|
||||||
|
)
|
||||||
|
|
||||||
|
val accept = FuncArrow(
|
||||||
|
"accept",
|
||||||
|
acceptBody,
|
||||||
|
ArrowType(
|
||||||
|
ProductType.labelled("Outer" -> outerType :: Nil),
|
||||||
|
ProductType(ScalarType.bool :: Nil)
|
||||||
|
),
|
||||||
|
resVar :: Nil,
|
||||||
|
Map.empty,
|
||||||
|
Map.empty,
|
||||||
|
None
|
||||||
|
)
|
||||||
|
|
||||||
|
val closureBody = SeqTag.wrap(
|
||||||
|
AssignmentTag(
|
||||||
|
CallArrowRaw.service(
|
||||||
|
"cmp",
|
||||||
|
LiteralRaw.quote("cmp"),
|
||||||
|
"gt",
|
||||||
|
ArrowType(
|
||||||
|
ProductType(ScalarType.i64 :: ScalarType.i64 :: Nil),
|
||||||
|
ProductType(ScalarType.bool :: Nil)
|
||||||
|
),
|
||||||
|
List(
|
||||||
|
VarRaw("x", ScalarType.i8),
|
||||||
|
LiteralRaw.number(0)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
"res"
|
||||||
|
).leaf,
|
||||||
|
ReturnTag(NonEmptyList.one(resVar)).leaf
|
||||||
|
)
|
||||||
|
|
||||||
|
val mainBody = SeqTag.wrap(
|
||||||
|
ClosureTag(
|
||||||
|
FuncRaw(
|
||||||
|
name = "closure",
|
||||||
|
arrow = ArrowRaw(
|
||||||
|
`type` = closureType,
|
||||||
|
ret = resVar :: Nil,
|
||||||
|
body = closureBody
|
||||||
|
)
|
||||||
|
),
|
||||||
|
detach = true
|
||||||
|
).leaf,
|
||||||
|
AssignmentTag(
|
||||||
|
AbilityRaw(
|
||||||
|
NonEmptyMap.of(
|
||||||
|
"arrow" -> VarRaw("closure", closureType),
|
||||||
|
"field" -> LiteralRaw.number(42)
|
||||||
|
),
|
||||||
|
innerType
|
||||||
|
),
|
||||||
|
"inner"
|
||||||
|
).leaf,
|
||||||
|
AssignmentTag(
|
||||||
|
AbilityRaw(
|
||||||
|
NonEmptyMap.of(
|
||||||
|
"inner" -> VarRaw("inner", innerType)
|
||||||
|
),
|
||||||
|
outerType
|
||||||
|
),
|
||||||
|
"outer"
|
||||||
|
).leaf,
|
||||||
|
CallArrowRawTag
|
||||||
|
.func(
|
||||||
|
"accept",
|
||||||
|
Call(
|
||||||
|
VarRaw("outer", outerType) :: Nil,
|
||||||
|
Call.Export(resVar.name, resVar.`type`) :: Nil
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.leaf,
|
||||||
|
ReturnTag(NonEmptyList.one(resVar)).leaf
|
||||||
|
)
|
||||||
|
|
||||||
|
val main = FuncArrow(
|
||||||
|
"main",
|
||||||
|
mainBody,
|
||||||
|
ArrowType(
|
||||||
|
ProductType(Nil),
|
||||||
|
ProductType(ScalarType.bool :: Nil)
|
||||||
|
),
|
||||||
|
resVar :: Nil,
|
||||||
|
Map("accept" -> accept),
|
||||||
|
Map.empty,
|
||||||
|
None
|
||||||
|
)
|
||||||
|
|
||||||
|
val model = ArrowInliner
|
||||||
|
.callArrow[InliningState](
|
||||||
|
FuncArrow(
|
||||||
|
"wrapper",
|
||||||
|
CallArrowRawTag
|
||||||
|
.func(
|
||||||
|
"main",
|
||||||
|
Call(Nil, Nil)
|
||||||
|
)
|
||||||
|
.leaf,
|
||||||
|
ArrowType(
|
||||||
|
ProductType(Nil),
|
||||||
|
ProductType(Nil)
|
||||||
|
),
|
||||||
|
Nil,
|
||||||
|
Map("main" -> main),
|
||||||
|
Map.empty,
|
||||||
|
None
|
||||||
|
),
|
||||||
|
CallModel(Nil, Nil)
|
||||||
|
)
|
||||||
|
.runA(InliningState())
|
||||||
|
.value
|
||||||
|
|
||||||
|
val body = SeqModel.wrap(
|
||||||
|
SeqModel.wrap(
|
||||||
|
FlattenModel(LiteralModel.number(42), "literal_ap").leaf,
|
||||||
|
FlattenModel(VarModel("literal_ap", LiteralType.unsigned), "literal_props").leaf
|
||||||
|
),
|
||||||
|
CallServiceModel(
|
||||||
|
LiteralModel.quote("cmp"),
|
||||||
|
"gt",
|
||||||
|
CallModel(
|
||||||
|
VarModel("literal_props", LiteralType.unsigned) :: LiteralModel.number(0) :: Nil,
|
||||||
|
CallModel.Export("gt", ScalarType.bool) :: Nil
|
||||||
|
)
|
||||||
|
).leaf
|
||||||
|
)
|
||||||
|
|
||||||
|
val expected = List("main", "accept", "closure").foldRight(body) { case (name, body) =>
|
||||||
|
MetaModel.CallArrowModel(name).wrap(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
model.equalsOrShowDiff(expected) shouldEqual true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,10 @@ package aqua.types
|
|||||||
|
|
||||||
import cats.PartialOrder
|
import cats.PartialOrder
|
||||||
import cats.data.NonEmptyMap
|
import cats.data.NonEmptyMap
|
||||||
|
import cats.Eval
|
||||||
|
import cats.syntax.traverse.*
|
||||||
|
import cats.syntax.applicative.*
|
||||||
|
import cats.syntax.option.*
|
||||||
|
|
||||||
sealed trait Type {
|
sealed trait Type {
|
||||||
|
|
||||||
@ -252,18 +256,69 @@ case class StructType(name: String, fields: NonEmptyMap[String, Type])
|
|||||||
// Ability is an unordered collection of labelled types and arrows
|
// Ability is an unordered collection of labelled types and arrows
|
||||||
case class AbilityType(name: String, fields: NonEmptyMap[String, Type]) extends NamedType {
|
case class AbilityType(name: String, fields: NonEmptyMap[String, Type]) extends NamedType {
|
||||||
|
|
||||||
lazy val arrows: Map[String, ArrowType] = fields.toNel.collect {
|
/**
|
||||||
case (name, at @ ArrowType(_, _)) => (name, at)
|
* Get all arrows defined in this ability and its sub-abilities.
|
||||||
}.toMap
|
* Paths to arrows are returned **without** ability name
|
||||||
|
* to allow renaming on call site.
|
||||||
|
*/
|
||||||
|
lazy val arrows: Map[String, ArrowType] = {
|
||||||
|
def getArrowsEval(path: Option[String], ability: AbilityType): Eval[List[(String, ArrowType)]] =
|
||||||
|
ability.fields.toNel.toList.flatTraverse {
|
||||||
|
case (abName, abType: AbilityType) =>
|
||||||
|
val newPath = path.fold(abName)(AbilityType.fullName(_, abName))
|
||||||
|
getArrowsEval(newPath.some, abType)
|
||||||
|
case (aName, aType: ArrowType) =>
|
||||||
|
val newPath = path.fold(aName)(AbilityType.fullName(_, aName))
|
||||||
|
List(newPath -> aType).pure
|
||||||
|
case _ => Nil.pure
|
||||||
|
}
|
||||||
|
|
||||||
lazy val abilities: List[(String, AbilityType)] = fields.toNel.collect {
|
getArrowsEval(None, this).value.toMap
|
||||||
case (name, at @ AbilityType(_, _)) => (name, at)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy val variables: List[(String, Type)] = fields.toNel.filter {
|
/**
|
||||||
case (_, AbilityType(_, _)) => false
|
* Get all abilities defined in this ability and its sub-abilities.
|
||||||
case (_, ArrowType(_, _)) => false
|
* Paths to abilities are returned **without** ability name
|
||||||
case (_, _) => true
|
* to allow renaming on call site.
|
||||||
|
*/
|
||||||
|
lazy val abilities: Map[String, AbilityType] = {
|
||||||
|
def getAbilitiesEval(
|
||||||
|
path: Option[String],
|
||||||
|
ability: AbilityType
|
||||||
|
): Eval[List[(String, AbilityType)]] =
|
||||||
|
ability.fields.toNel.toList.flatTraverse {
|
||||||
|
case (abName, abType: AbilityType) =>
|
||||||
|
val fullName = path.fold(abName)(AbilityType.fullName(_, abName))
|
||||||
|
getAbilitiesEval(fullName.some, abType).map(
|
||||||
|
(fullName -> abType) :: _
|
||||||
|
)
|
||||||
|
case _ => Nil.pure
|
||||||
|
}
|
||||||
|
|
||||||
|
getAbilitiesEval(None, this).value.toMap
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all variables defined in this ability and its sub-abilities.
|
||||||
|
* Paths to variables are returned **without** ability name
|
||||||
|
* to allow renaming on call site.
|
||||||
|
*/
|
||||||
|
lazy val variables: Map[String, DataType] = {
|
||||||
|
def getVariablesEval(
|
||||||
|
path: Option[String],
|
||||||
|
ability: AbilityType
|
||||||
|
): Eval[List[(String, DataType)]] =
|
||||||
|
ability.fields.toNel.toList.flatTraverse {
|
||||||
|
case (abName, abType: AbilityType) =>
|
||||||
|
val newPath = path.fold(abName)(AbilityType.fullName(_, abName))
|
||||||
|
getVariablesEval(newPath.some, abType)
|
||||||
|
case (dName, dType: DataType) =>
|
||||||
|
val newPath = path.fold(dName)(AbilityType.fullName(_, dName))
|
||||||
|
List(newPath -> dType).pure
|
||||||
|
case _ => Nil.pure
|
||||||
|
}
|
||||||
|
|
||||||
|
getVariablesEval(None, this).value.toMap
|
||||||
}
|
}
|
||||||
|
|
||||||
override def toString: String =
|
override def toString: String =
|
||||||
|
Loading…
Reference in New Issue
Block a user