mirror of
https://github.com/fluencelabs/aqua.git
synced 2024-12-04 14:40:17 +00:00
feat(compiler): Structural typing for data and abilities [fixes LNG-215] (#843)
This commit is contained in:
parent
eb4cdb0dd1
commit
019611a89c
@ -1,7 +1,35 @@
|
||||
service Srv("srv"):
|
||||
call(x: i32) -> i32
|
||||
aqua Aaa
|
||||
|
||||
func main() -> i32:
|
||||
arr = [1, 2, 3]
|
||||
a <- Srv.call(0)
|
||||
<- arr[Srv.call(1)]
|
||||
import "builtin.aqua"
|
||||
|
||||
export structuralTypingTest
|
||||
|
||||
data WideData:
|
||||
s: string
|
||||
n: u32
|
||||
|
||||
data ExactData:
|
||||
s: string
|
||||
|
||||
ability ExactAbility:
|
||||
s: string
|
||||
arr(s: string, s2: string, s3: string, s4: string) -> string
|
||||
exact: ExactData
|
||||
|
||||
ability WideAbility:
|
||||
s: string
|
||||
arr(s: string, s2: string, s3: string, s4: string) -> string
|
||||
g: string
|
||||
exact: WideData
|
||||
|
||||
func ss(s1: string, s2: string, s3: string, s4: string) -> string:
|
||||
<- Op.concat_strings(Op.concat_strings(Op.concat_strings(s1, s2), s3), s4)
|
||||
|
||||
func main{ExactAbility}(someData: ExactData, secondData: ExactData) -> string:
|
||||
<- ExactAbility.arr(someData.s, ExactAbility.exact.s, secondData.s, ExactAbility.s)
|
||||
|
||||
func structuralTypingTest() -> string:
|
||||
wd = WideData(s = "some_string", n = 32)
|
||||
|
||||
WAbility = WideAbility(s = "ab_string", g = "", arr = ss, exact = wd)
|
||||
<- main{WAbility}(wd, WAbility.exact)
|
||||
|
35
integration-tests/aqua/examples/structuraltyping.aqua
Normal file
35
integration-tests/aqua/examples/structuraltyping.aqua
Normal file
@ -0,0 +1,35 @@
|
||||
aqua Aaa
|
||||
|
||||
import "@fluencelabs/aqua-lib/builtin.aqua"
|
||||
|
||||
export structuralTypingTest
|
||||
|
||||
data WideData:
|
||||
s: string
|
||||
n: u32
|
||||
|
||||
data ExactData:
|
||||
s: string
|
||||
|
||||
ability ExactAbility:
|
||||
s: string
|
||||
arr(s: string, s2: string, s3: string, s4: string) -> string
|
||||
exact: ExactData
|
||||
|
||||
ability WideAbility:
|
||||
s: string
|
||||
arr(s: string, s2: string, s3: string, s4: string) -> string
|
||||
g: string
|
||||
exact: WideData
|
||||
|
||||
func ss(s1: string, s2: string, s3: string, s4: string) -> string:
|
||||
<- Op.concat_strings(Op.concat_strings(Op.concat_strings(s1, s2), s3), s4)
|
||||
|
||||
func main{ExactAbility}(someData: ExactData, secondData: ExactData) -> string:
|
||||
<- ExactAbility.arr(someData.s, ExactAbility.exact.s, secondData.s, ExactAbility.s)
|
||||
|
||||
func structuralTypingTest() -> string:
|
||||
wd = WideData(s = "some_string", n = 32)
|
||||
|
||||
WAbility = WideAbility(s = "ab_string", g = "", arr = ss, exact = wd)
|
||||
<- main{WAbility}(wd, WAbility.exact)
|
@ -80,6 +80,7 @@ export const relay2 = config.relays[1];
|
||||
const relayPeerId2 = relay2.peerId;
|
||||
|
||||
import log from 'loglevel';
|
||||
import {structuralTypingCall} from "../examples/structuralTypingCall";
|
||||
// log.setDefaultLevel("debug")
|
||||
|
||||
async function start() {
|
||||
@ -244,6 +245,11 @@ describe('Testing examples', () => {
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('structuraltyping.aqua', async () => {
|
||||
let result = await structuralTypingCall();
|
||||
expect(result).toEqual("some_stringsome_stringsome_stringab_string");
|
||||
});
|
||||
|
||||
it('collectionSugar array', async () => {
|
||||
let result = await arraySugarCall();
|
||||
expect(result).toEqual([
|
||||
|
5
integration-tests/src/examples/structuralTypingCall.ts
Normal file
5
integration-tests/src/examples/structuralTypingCall.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import {structuralTypingTest} from "../compiled/examples/structuraltyping";
|
||||
|
||||
export async function structuralTypingCall(): Promise<string> {
|
||||
return await structuralTypingTest();
|
||||
}
|
@ -1,16 +1,16 @@
|
||||
package aqua.model.inline
|
||||
|
||||
import aqua.model
|
||||
import aqua.model.inline.state.{Arrows, Exports, Mangler}
|
||||
import aqua.model.*
|
||||
import aqua.model.inline.state.{Arrows, Exports, Mangler}
|
||||
import aqua.raw.ops.RawTag
|
||||
import aqua.types.{AbilityType, ArrowType, BoxType, StreamType}
|
||||
import aqua.raw.value.{ValueRaw, VarRaw}
|
||||
import cats.{Eval, Monoid}
|
||||
import aqua.types.{AbilityType, ArrowType, BoxType, StreamType}
|
||||
import cats.data.{Chain, IndexedStateT, State}
|
||||
import cats.syntax.traverse.*
|
||||
import cats.syntax.bifunctor.*
|
||||
import cats.syntax.foldable.*
|
||||
import cats.syntax.traverse.*
|
||||
import cats.{Eval, Monoid}
|
||||
import scribe.Logging
|
||||
|
||||
/**
|
||||
@ -267,7 +267,7 @@ object ArrowInliner extends Logging {
|
||||
): State[S, AbilityResolvingResult] = {
|
||||
for {
|
||||
newName <- Mangler[S].findNewName(name)
|
||||
newFieldsName = t.fields.mapBoth { case (n, t) =>
|
||||
newFieldsName = t.fields.mapBoth { case (n, _) =>
|
||||
AbilityType.fullName(name, n) -> AbilityType.fullName(newName, n)
|
||||
}
|
||||
allNewNames = newFieldsName.add((name, newName)).toSortedMap
|
||||
@ -313,22 +313,28 @@ object ArrowInliner extends Logging {
|
||||
case ArrowType(_, _) =>
|
||||
Exports
|
||||
.getLastValue(currentOldName, exports)
|
||||
.flatMap { case vm @ VarModel(name, _, _) =>
|
||||
arrows
|
||||
.get(name)
|
||||
.map(fa =>
|
||||
(
|
||||
Map(currentNewName.getOrElse(currentOldName) -> vm),
|
||||
Map(name -> fa)
|
||||
.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 _ =>
|
||||
Exports
|
||||
.getLastValue(currentOldName, exports)
|
||||
.map(vm => (Map(currentNewName.getOrElse(currentOldName) -> vm), Map.empty))
|
||||
.map { vm =>
|
||||
(Map(currentNewName.getOrElse(currentOldName) -> vm), Map.empty)
|
||||
}
|
||||
.getOrElse((Map.empty, Map.empty))
|
||||
}
|
||||
}.foldMapA(_.bimap(_.toList, _.toList)).bimap(_.toMap, _.toMap)
|
||||
|
@ -76,7 +76,7 @@ object CallArrowRawInliner extends RawInliner[CallArrowRaw] with Logging {
|
||||
): State[S, (List[ValueModel], Inline)] = for {
|
||||
arrows <- Arrows[S].arrows
|
||||
exports <- Exports[S].exports
|
||||
lastArrow <- Exports[S].getLast(funcName)
|
||||
lastArrow <- Exports[S].getLastVarName(funcName)
|
||||
arrow = arrows
|
||||
.get(funcName)
|
||||
.orElse(
|
||||
|
@ -1,6 +1,6 @@
|
||||
package aqua.model.inline.state
|
||||
|
||||
import aqua.model.{ValueModel, VarModel}
|
||||
import aqua.model.{LiteralModel, ValueModel, VarModel}
|
||||
import aqua.model.ValueModel.Ability
|
||||
import aqua.types.AbilityType
|
||||
import cats.data.{NonEmptyList, State}
|
||||
@ -44,9 +44,9 @@ trait Exports[S] extends Scoped[S] {
|
||||
def copyWithAbilityPrefix(prefix: String, newPrefix: String): State[S, Unit]
|
||||
|
||||
/**
|
||||
* Get name of last linked VarModel
|
||||
* Get name of last linked VarModel. If the last element is not VarModel, return None
|
||||
*/
|
||||
def getLast(name: String): State[S, Option[String]]
|
||||
def getLastVarName(name: String): State[S, Option[String]]
|
||||
|
||||
/**
|
||||
* Rename names in variables
|
||||
@ -98,8 +98,8 @@ trait Exports[S] extends Scoped[S] {
|
||||
override def copyWithAbilityPrefix(prefix: String, newPrefix: String): State[R, Unit] =
|
||||
self.copyWithAbilityPrefix(prefix, newPrefix).transformS(f, g)
|
||||
|
||||
override def getLast(name: String): State[R, Option[String]] =
|
||||
self.getLast(name).transformS(f, g)
|
||||
override def getLastVarName(name: String): State[R, Option[String]] =
|
||||
self.getLastVarName(name).transformS(f, g)
|
||||
|
||||
override def renameVariables(renames: Map[String, String]): State[R, Unit] =
|
||||
self.renameVariables(renames).transformS(f, g)
|
||||
@ -125,12 +125,14 @@ object Exports {
|
||||
def apply[S](implicit exports: Exports[S]): Exports[S] = exports
|
||||
|
||||
// Get last linked VarModel
|
||||
def getLastValue(name: String, state: Map[String, ValueModel]): Option[VarModel] = {
|
||||
def getLastValue(name: String, state: Map[String, ValueModel]): Option[ValueModel] = {
|
||||
state.get(name) match {
|
||||
case Some(vm@VarModel(n, _, _)) =>
|
||||
if (name == n) Option(vm)
|
||||
else getLastValue(n, state).orElse(Option(vm))
|
||||
case n =>
|
||||
case lm@Some(LiteralModel(_, _)) =>
|
||||
lm
|
||||
case _ =>
|
||||
None
|
||||
}
|
||||
}
|
||||
@ -138,7 +140,7 @@ object Exports {
|
||||
object Simple extends Exports[Map[String, ValueModel]] {
|
||||
|
||||
// Make links from one set of abilities to another (for ability assignment)
|
||||
private def getAbilityPairs(oldName: String, newName: String, at: AbilityType, state: Map[String, ValueModel]): NonEmptyList[(String, VarModel)] = {
|
||||
private def getAbilityPairs(oldName: String, newName: String, at: AbilityType, state: Map[String, ValueModel]): NonEmptyList[(String, ValueModel)] = {
|
||||
at.fields.toNel.flatMap {
|
||||
case (n, at@AbilityType(_, _)) =>
|
||||
val newFullName = AbilityType.fullName(newName, n)
|
||||
@ -158,15 +160,19 @@ object Exports {
|
||||
value: ValueModel
|
||||
): State[Map[String, ValueModel], Unit] = State.modify { state =>
|
||||
value match {
|
||||
case vm@Ability(name, at, property) if property.isEmpty =>
|
||||
case Ability(name, at, property) if property.isEmpty =>
|
||||
val pairs = getAbilityPairs(name, exportName, at, state)
|
||||
state ++ pairs.toList.toMap
|
||||
case _ => state + (exportName -> value)
|
||||
}
|
||||
}
|
||||
|
||||
override def getLast(name: String): State[Map[String, ValueModel], Option[String]] =
|
||||
State.get.map(st => getLastValue(name, st).map(_.name))
|
||||
override def getLastVarName(name: String): State[Map[String, ValueModel], Option[String]] =
|
||||
State.get.map(st => getLastValue(name, st).flatMap {
|
||||
case VarModel(name, _, _) => Option(name)
|
||||
case LiteralModel(_, _) =>
|
||||
None
|
||||
})
|
||||
|
||||
override def resolved(exports: Map[String, ValueModel]): State[Map[String, ValueModel], Unit] =
|
||||
State.modify(_ ++ exports)
|
||||
|
@ -63,19 +63,23 @@ object CompareTypes {
|
||||
val lfView = lf.view
|
||||
val rfView = rf.view
|
||||
if (lf == rf) 0.0
|
||||
else if (
|
||||
lf.keys.forall(rf.contains) && compareTypesList(
|
||||
lfView.values.toList,
|
||||
rfView.filterKeys(lfNEM.keys.contains).values.toList
|
||||
) == 1.0
|
||||
) 1.0
|
||||
else if (
|
||||
rf.keys.forall(lf.contains) && compareTypesList(
|
||||
lfView.filterKeys(rfNEM.keys.contains).values.toList,
|
||||
rfView.values.toList
|
||||
) == -1.0
|
||||
) -1.0
|
||||
else NaN
|
||||
else if (lf.keys.forall(rf.contains)) {
|
||||
if (
|
||||
compareTypesList(
|
||||
lfView.values.toList,
|
||||
rfView.filterKeys(lfNEM.keys.contains).values.toList
|
||||
) >= 0.0
|
||||
) 1.0
|
||||
else NaN
|
||||
} else if (rf.keys.forall(lf.contains)) {
|
||||
if (
|
||||
compareTypesList(
|
||||
lfView.filterKeys(rfNEM.keys.contains).values.toList,
|
||||
rfView.values.toList
|
||||
) <= 0
|
||||
) -1.0
|
||||
else NaN
|
||||
} else NaN
|
||||
}
|
||||
|
||||
private def compareProducts(l: ProductType, r: ProductType): Double = ((l, r): @unchecked) match {
|
||||
|
Loading…
Reference in New Issue
Block a user