mirror of
https://github.com/fluencelabs/aqua.git
synced 2024-12-04 22:50:18 +00:00
fix(compiler): Nested abilities [fixes LNG-214] (#816)
This commit is contained in:
parent
dba12b8277
commit
4e3e70f4fc
@ -1,11 +1,87 @@
|
|||||||
aqua Main
|
aqua Main
|
||||||
|
|
||||||
export a
|
use DECLARE_CONST, decl_bar from "declare.aqua" as Declare
|
||||||
|
|
||||||
alias CL: string -> ()
|
export SomeService, handleAb, bug214, checkAbCalls
|
||||||
|
|
||||||
func a(cl: string -> ()) -> CL:
|
service SomeService("wed"):
|
||||||
<- cl
|
getStr(s: string) -> string
|
||||||
|
|
||||||
func b(c: u32, d: u32) -> u32:
|
ability SomeAb:
|
||||||
<- c + d
|
someArrow(s: string) -> string, string
|
||||||
|
str: string
|
||||||
|
|
||||||
|
ability SecondAb:
|
||||||
|
arrow(s: string) -> string
|
||||||
|
num: u32
|
||||||
|
|
||||||
|
func funcStr(s: string) -> string, string:
|
||||||
|
strInFunc <- SomeService.getStr(Declare.DECLARE_CONST)
|
||||||
|
strInFunc2 <- SomeService.getStr(s)
|
||||||
|
<- strInFunc, strInFunc2
|
||||||
|
|
||||||
|
func handleSecAb {SomeAb, SecondAb}() -> string, string, string, u32:
|
||||||
|
SomeAb.someArrow("eferfrfrf")
|
||||||
|
b, c <- SomeAb.someArrow("efre")
|
||||||
|
d <- SecondAb.arrow(SomeAb.str)
|
||||||
|
<- b, c, d, SecondAb.num
|
||||||
|
|
||||||
|
func returnAb(s: string) -> SomeAb:
|
||||||
|
SomeAb = SomeAb(someArrow = funcStr, str = s)
|
||||||
|
<- SomeAb
|
||||||
|
|
||||||
|
func handleAb(fff: string) -> string, string, string, u32:
|
||||||
|
SomeAb = returnAb(fff)
|
||||||
|
SecondAb = SecondAb(arrow = funcStr, num = 12)
|
||||||
|
res1, res2, res3, res4 <- handleSecAb{SomeAb, SecondAb}()
|
||||||
|
<- res1, res2, res3, res4
|
||||||
|
|
||||||
|
data Struct:
|
||||||
|
int: i8
|
||||||
|
|
||||||
|
ability Simple:
|
||||||
|
st: Struct
|
||||||
|
arrow(x: i8) -> bool
|
||||||
|
|
||||||
|
ability Complex:
|
||||||
|
simple: Simple
|
||||||
|
field: string
|
||||||
|
|
||||||
|
func foo{Complex, Simple}() -> bool, bool:
|
||||||
|
closure = () -> bool:
|
||||||
|
<- Simple.st.int >= 0
|
||||||
|
res <- closure()
|
||||||
|
<- Complex.simple.arrow(
|
||||||
|
Complex.simple.st.int
|
||||||
|
), res
|
||||||
|
|
||||||
|
func bug214() -> bool, bool:
|
||||||
|
closure = (x: i8) -> bool:
|
||||||
|
<- x > 0
|
||||||
|
|
||||||
|
MyComplex = Complex(
|
||||||
|
simple = Simple(
|
||||||
|
st = Struct(int = 0),
|
||||||
|
arrow = closure
|
||||||
|
),
|
||||||
|
field = "complex"
|
||||||
|
)
|
||||||
|
|
||||||
|
res1, res2 <- foo{MyComplex, MyComplex.simple}()
|
||||||
|
<- res1, res2
|
||||||
|
|
||||||
|
ability SSS:
|
||||||
|
arrow(x: i8) -> bool
|
||||||
|
|
||||||
|
ability CCCC:
|
||||||
|
arrow(x: i8) -> bool
|
||||||
|
simple: SSS
|
||||||
|
|
||||||
|
func checkAbCalls() -> bool, bool:
|
||||||
|
closure = (x: i8) -> bool:
|
||||||
|
<- x > 20
|
||||||
|
|
||||||
|
MySSS = SSS(arrow = closure)
|
||||||
|
MyCCCC = CCCC(simple = MySSS, arrow = MySSS.arrow)
|
||||||
|
|
||||||
|
<- MySSS.arrow(42), MyCCCC.arrow(12)
|
||||||
|
@ -2,7 +2,7 @@ aqua Main
|
|||||||
|
|
||||||
use DECLARE_CONST, decl_bar from "imports_exports/declare.aqua" as Declare
|
use DECLARE_CONST, decl_bar from "imports_exports/declare.aqua" as Declare
|
||||||
|
|
||||||
export handleAb, SomeService
|
export handleAb, SomeService, bug214, checkAbCalls
|
||||||
|
|
||||||
service SomeService("wed"):
|
service SomeService("wed"):
|
||||||
getStr(s: string) -> string
|
getStr(s: string) -> string
|
||||||
@ -35,3 +35,53 @@ func handleAb(fff: string) -> string, string, string, u32:
|
|||||||
SecondAb = SecondAb(arrow = funcStr, num = 12)
|
SecondAb = SecondAb(arrow = funcStr, num = 12)
|
||||||
res1, res2, res3, res4 <- handleSecAb{SomeAb, SecondAb}()
|
res1, res2, res3, res4 <- handleSecAb{SomeAb, SecondAb}()
|
||||||
<- res1, res2, res3, res4
|
<- res1, res2, res3, res4
|
||||||
|
|
||||||
|
data Struct:
|
||||||
|
int: i8
|
||||||
|
|
||||||
|
ability Simple:
|
||||||
|
st: Struct
|
||||||
|
arrow(x: i8) -> bool
|
||||||
|
|
||||||
|
ability Complex:
|
||||||
|
simple: Simple
|
||||||
|
field: string
|
||||||
|
|
||||||
|
func foo{Complex, Simple}() -> bool, bool:
|
||||||
|
closure = () -> bool:
|
||||||
|
<- Simple.st.int >= 0
|
||||||
|
res <- closure()
|
||||||
|
<- Complex.simple.arrow(
|
||||||
|
Complex.simple.st.int
|
||||||
|
), res
|
||||||
|
|
||||||
|
func bug214() -> bool, bool:
|
||||||
|
closure = (x: i8) -> bool:
|
||||||
|
<- x > 0
|
||||||
|
|
||||||
|
MyComplex = Complex(
|
||||||
|
simple = Simple(
|
||||||
|
st = Struct(int = 0),
|
||||||
|
arrow = closure
|
||||||
|
),
|
||||||
|
field = "complex"
|
||||||
|
)
|
||||||
|
|
||||||
|
res1, res2 <- foo{MyComplex, MyComplex.simple}()
|
||||||
|
<- res1, res2
|
||||||
|
|
||||||
|
ability SSS:
|
||||||
|
arrow(x: i8) -> bool
|
||||||
|
|
||||||
|
ability CCCC:
|
||||||
|
arrow(x: i8) -> bool
|
||||||
|
simple: SSS
|
||||||
|
|
||||||
|
func checkAbCalls() -> bool, bool:
|
||||||
|
closure = (x: i8) -> bool:
|
||||||
|
<- x > 20
|
||||||
|
|
||||||
|
MySSS = SSS(arrow = closure)
|
||||||
|
MyCCCC = CCCC(simple = MySSS, arrow = MySSS.arrow)
|
||||||
|
|
||||||
|
<- MySSS.arrow(42), MyCCCC.arrow(12)
|
||||||
|
@ -13,6 +13,7 @@ import { bugNG69Call, ifCall, ifWrapCall } from '../examples/ifCall.js';
|
|||||||
import { parCall, testTimeoutCall } from '../examples/parCall.js';
|
import { parCall, testTimeoutCall } from '../examples/parCall.js';
|
||||||
import { complexCall } from '../examples/complex.js';
|
import { complexCall } from '../examples/complex.js';
|
||||||
import { constantsCall, particleTtlAndTimestampCall } from '../examples/constantsCall.js';
|
import { constantsCall, particleTtlAndTimestampCall } from '../examples/constantsCall.js';
|
||||||
|
import { abilityCall, complexAbilityCall, checkAbCallsCall } from '../examples/abilityCall.js';
|
||||||
import {
|
import {
|
||||||
nilLengthCall,
|
nilLengthCall,
|
||||||
nilLiteralCall,
|
nilLiteralCall,
|
||||||
@ -77,7 +78,6 @@ export const relay2 = config.relays[1];
|
|||||||
const relayPeerId2 = relay2.peerId;
|
const relayPeerId2 = relay2.peerId;
|
||||||
|
|
||||||
import log from 'loglevel';
|
import log from 'loglevel';
|
||||||
import { abilityCall } from '../examples/abilityCall';
|
|
||||||
// log.setDefaultLevel("debug")
|
// log.setDefaultLevel("debug")
|
||||||
|
|
||||||
async function start() {
|
async function start() {
|
||||||
@ -354,6 +354,16 @@ describe('Testing examples', () => {
|
|||||||
expect(result).toStrictEqual(['declare_const123', 'efre123', 'declare_const123', 12]);
|
expect(result).toStrictEqual(['declare_const123', 'efre123', 'declare_const123', 12]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('ability.aqua complex', async () => {
|
||||||
|
let result = await complexAbilityCall();
|
||||||
|
expect(result).toStrictEqual([false, true]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ability.aqua complex', async () => {
|
||||||
|
let result = await checkAbCallsCall();
|
||||||
|
expect(result).toStrictEqual([true, false]);
|
||||||
|
});
|
||||||
|
|
||||||
it('functors.aqua LNG-119 bug', async () => {
|
it('functors.aqua LNG-119 bug', async () => {
|
||||||
let result = await bugLng119Call();
|
let result = await bugLng119Call();
|
||||||
expect(result).toEqual([1]);
|
expect(result).toEqual([1]);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {handleAb, registerSomeService} from "../compiled/examples/abilities";
|
import {handleAb, registerSomeService, bug214, checkAbCalls} from "../compiled/examples/abilities";
|
||||||
|
|
||||||
export async function abilityCall(): Promise<[string, string, string, number]> {
|
export async function abilityCall(): Promise<[string, string, string, number]> {
|
||||||
registerSomeService({
|
registerSomeService({
|
||||||
@ -9,3 +9,11 @@ export async function abilityCall(): Promise<[string, string, string, number]> {
|
|||||||
|
|
||||||
return await handleAb("some_string")
|
return await handleAb("some_string")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function complexAbilityCall(): Promise<[boolean, boolean]> {
|
||||||
|
return await bug214()
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function checkAbCallsCall(): Promise<[boolean, boolean]> {
|
||||||
|
return await checkAbCalls()
|
||||||
|
}
|
||||||
|
@ -6,7 +6,7 @@ import aqua.model.*
|
|||||||
import aqua.raw.ops.RawTag
|
import aqua.raw.ops.RawTag
|
||||||
import aqua.types.{AbilityType, ArrowType, BoxType, DataType, StreamType, Type}
|
import aqua.types.{AbilityType, ArrowType, BoxType, DataType, StreamType, Type}
|
||||||
import aqua.raw.value.{ValueRaw, VarRaw}
|
import aqua.raw.value.{ValueRaw, VarRaw}
|
||||||
import cats.Eval
|
import cats.{Eval, Monoid}
|
||||||
import cats.data.{Chain, IndexedStateT, State}
|
import cats.data.{Chain, IndexedStateT, State}
|
||||||
import cats.syntax.traverse.*
|
import cats.syntax.traverse.*
|
||||||
import cats.syntax.apply.*
|
import cats.syntax.apply.*
|
||||||
@ -91,59 +91,50 @@ object ArrowInliner extends Logging {
|
|||||||
// Apply a callable function, get its fully resolved body & optional value, if any
|
// Apply a callable function, get its fully resolved body & optional value, if any
|
||||||
private def inline[S: Mangler: Arrows: Exports](
|
private def inline[S: Mangler: Arrows: Exports](
|
||||||
fn: FuncArrow,
|
fn: FuncArrow,
|
||||||
call: CallModel
|
call: CallModel,
|
||||||
|
outsideDeclaredStreams: Set[String]
|
||||||
): State[S, InlineResult] =
|
): State[S, InlineResult] =
|
||||||
(Exports[S].exports, getOutsideStreamNames).flatMapN {
|
for {
|
||||||
case (oldExports, outsideDeclaredStreams) =>
|
// Register captured values as available exports
|
||||||
// Function's internal variables will not be available outside, hence the scope
|
_ <- Exports[S].resolved(fn.capturedValues)
|
||||||
Exports[S].scope(
|
_ <- Mangler[S].forbid(fn.capturedValues.keySet)
|
||||||
for {
|
|
||||||
// Process renamings, prepare environment
|
|
||||||
tr <- prelude[S](fn, call, oldExports)
|
|
||||||
(tree, results) = tr
|
|
||||||
|
|
||||||
// Register captured values as available exports
|
// Now, substitute the arrows that were received as function arguments
|
||||||
_ <- Exports[S].resolved(fn.capturedValues)
|
// Use the new op tree (args are replaced with values, names are unique & safe)
|
||||||
_ <- Mangler[S].forbid(fn.capturedValues.keySet)
|
callableFuncBodyNoTopology <- TagInliner.handleTree(fn.body, fn.funcName)
|
||||||
|
callableFuncBody =
|
||||||
|
fn.capturedTopology
|
||||||
|
.fold[OpModel](SeqModel)(ApplyTopologyModel.apply)
|
||||||
|
.wrap(callableFuncBodyNoTopology)
|
||||||
|
|
||||||
// Now, substitute the arrows that were received as function arguments
|
opsAndRets <- pushStreamResults(
|
||||||
// Use the new op tree (args are replaced with values, names are unique & safe)
|
outsideDeclaredStreams,
|
||||||
callableFuncBodyNoTopology <- TagInliner.handleTree(tree, fn.funcName)
|
call.exportTo,
|
||||||
callableFuncBody =
|
fn.ret,
|
||||||
fn.capturedTopology
|
callableFuncBody
|
||||||
.fold[OpModel](SeqModel)(ApplyTopologyModel.apply)
|
)
|
||||||
.wrap(callableFuncBodyNoTopology)
|
(ops, rets) = opsAndRets
|
||||||
|
|
||||||
opsAndRets <- pushStreamResults(
|
exports <- Exports[S].exports
|
||||||
outsideDeclaredStreams,
|
arrows <- Arrows[S].arrows
|
||||||
call.exportTo,
|
// gather all arrows and variables from abilities
|
||||||
results,
|
returnedFromAbilities = rets.collect { case VarModel(name, st @ AbilityType(_, _), _) =>
|
||||||
callableFuncBody
|
getVarsAndArrowsFromAbilities(name, None, st, exports, arrows)
|
||||||
)
|
}.foldMapA(_.bimap(_.toList, _.toList)).bimap(_.toMap, _.toMap)
|
||||||
(ops, rets) = opsAndRets
|
|
||||||
|
|
||||||
exports <- Exports[S].exports
|
// find and get resolved arrows if we return them from the function
|
||||||
arrows <- Arrows[S].arrows
|
returnedArrows = rets.collect { case VarModel(name, ArrowType(_, _), _) =>
|
||||||
// gather all arrows and variables from abilities
|
name
|
||||||
returnedFromAbilities = rets.collect { case VarModel(name, st @ AbilityType(_, _), _) =>
|
}.toSet
|
||||||
getVarsAndArrowsFromAbilities(name, None, st, exports, arrows)
|
arrowsToSave <- Arrows[S].pickArrows(returnedArrows)
|
||||||
}.foldMapA(_.bimap(_.toList, _.toList)).bimap(_.toMap, _.toMap)
|
} yield {
|
||||||
|
val (valsFromAbilities, arrowsFromAbilities) = returnedFromAbilities
|
||||||
// find and get resolved arrows if we return them from the function
|
InlineResult(
|
||||||
returnedArrows = rets.collect { case VarModel(name, ArrowType(_, _), _) =>
|
SeqModel.wrap(ops.reverse: _*),
|
||||||
name
|
rets.reverse,
|
||||||
}.toSet
|
valsFromAbilities,
|
||||||
arrowsToSave <- Arrows[S].pickArrows(returnedArrows)
|
arrowsFromAbilities ++ arrowsToSave
|
||||||
} yield {
|
)
|
||||||
val (valsFromAbilities, arrowsFromAbilities) = returnedFromAbilities
|
|
||||||
InlineResult(
|
|
||||||
SeqModel.wrap(ops.reverse: _*),
|
|
||||||
rets.reverse,
|
|
||||||
valsFromAbilities,
|
|
||||||
arrowsFromAbilities ++ arrowsToSave
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -243,32 +234,47 @@ object ArrowInliner extends Logging {
|
|||||||
renamedArrows: Map[String, FuncArrow]
|
renamedArrows: Map[String, FuncArrow]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
given Monoid[AbilityResolvingResult] with
|
||||||
|
|
||||||
|
override val empty: AbilityResolvingResult =
|
||||||
|
AbilityResolvingResult(Map.empty, Map.empty, Map.empty)
|
||||||
|
|
||||||
|
override def combine(
|
||||||
|
a: AbilityResolvingResult,
|
||||||
|
b: AbilityResolvingResult
|
||||||
|
): AbilityResolvingResult =
|
||||||
|
AbilityResolvingResult(
|
||||||
|
a.namesToRename ++ b.namesToRename,
|
||||||
|
a.renamedExports ++ b.renamedExports,
|
||||||
|
a.renamedArrows ++ b.renamedArrows
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate new names for all ability fields and arrows if necessary.
|
* Generate new names for all ability fields and arrows if necessary.
|
||||||
* Gather all fields and arrows from Arrows and Exports states
|
* Gather all fields and arrows from Arrows and Exports states
|
||||||
* @param name ability name in state
|
* @param name ability name in state
|
||||||
* @param vm ability variable
|
* @param vm ability variable
|
||||||
* @param t ability type
|
* @param t ability type
|
||||||
* @param oldExports previous Exports
|
* @param exports previous Exports
|
||||||
* @param oldArrows previous Arrows
|
* @param arrows previous Arrows
|
||||||
* @return names to rename, Exports and Arrows with all ability fields and arrows
|
* @return names to rename, Exports and Arrows with all ability fields and arrows
|
||||||
*/
|
*/
|
||||||
private def renameAndResolveAbilities[S: Mangler: Arrows: Exports](
|
private def renameAndResolveAbilities[S: Mangler: Arrows: Exports](
|
||||||
name: String,
|
name: String,
|
||||||
vm: VarModel,
|
vm: VarModel,
|
||||||
t: AbilityType,
|
t: AbilityType,
|
||||||
oldExports: Map[String, ValueModel],
|
exports: Map[String, ValueModel],
|
||||||
oldArrows: Map[String, FuncArrow]
|
arrows: Map[String, FuncArrow]
|
||||||
): State[S, AbilityResolvingResult] = {
|
): State[S, AbilityResolvingResult] = {
|
||||||
for {
|
for {
|
||||||
newName <- Mangler[S].findNewName(name)
|
newName <- Mangler[S].findNewName(name)
|
||||||
newFieldsName = t.fields.mapBoth { case (n, t) =>
|
newFieldsName = t.fields.mapBoth { case (n, t) =>
|
||||||
s"$name.$n" -> s"$newName.$n"
|
AbilityType.fullName(name, n) -> AbilityType.fullName(newName, n)
|
||||||
}
|
}
|
||||||
allNewNames = newFieldsName.add((name, newName)).toSortedMap
|
allNewNames = newFieldsName.add((name, newName)).toSortedMap
|
||||||
} yield {
|
} yield {
|
||||||
val (allVars, allArrows) =
|
val (allVars, allArrows) =
|
||||||
getVarsAndArrowsFromAbilities(vm.name, Option(newName), t, oldExports, oldArrows)
|
getVarsAndArrowsFromAbilities(vm.name, Option(newName), t, exports, arrows)
|
||||||
AbilityResolvingResult(allNewNames, allVars, allArrows)
|
AbilityResolvingResult(allNewNames, allVars, allArrows)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -281,58 +287,50 @@ object ArrowInliner extends Logging {
|
|||||||
* @param topOldName old name to find all fields in states
|
* @param topOldName old name to find all fields in states
|
||||||
* @param topNewName new name to rename all fields in states
|
* @param topNewName new name to rename all fields in states
|
||||||
* @param abilityType type of current ability
|
* @param abilityType type of current ability
|
||||||
* @param oldExports where to get values
|
* @param exports where to get values
|
||||||
* @param oldArrows where to get arrows
|
* @param arrows where to get arrows
|
||||||
* @param valAcc accumulator for values
|
|
||||||
* @param arrowsAcc accumulator for arrows
|
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private def getVarsAndArrowsFromAbilities(
|
private def getVarsAndArrowsFromAbilities(
|
||||||
topOldName: String,
|
topOldName: String,
|
||||||
topNewName: Option[String],
|
topNewName: Option[String],
|
||||||
abilityType: AbilityType,
|
abilityType: AbilityType,
|
||||||
oldExports: Map[String, ValueModel],
|
exports: Map[String, ValueModel],
|
||||||
oldArrows: Map[String, FuncArrow],
|
arrows: Map[String, FuncArrow]
|
||||||
valAcc: Map[String, ValueModel] = Map.empty,
|
|
||||||
arrowsAcc: Map[String, FuncArrow] = Map.empty
|
|
||||||
): (Map[String, ValueModel], Map[String, FuncArrow]) = {
|
): (Map[String, ValueModel], Map[String, FuncArrow]) = {
|
||||||
abilityType.fields.toSortedMap.toList.map { case (fName, fValue) =>
|
abilityType.fields.toSortedMap.toList.map { case (fName, fValue) =>
|
||||||
val currentOldName = s"$topOldName.$fName"
|
val currentOldName = AbilityType.fullName(topOldName, fName)
|
||||||
// for all nested fields, arrows and abilities only left side must be renamed
|
// for all nested fields, arrows and abilities only left side must be renamed
|
||||||
val currentNewName = topNewName.map(_ + s".$fName")
|
val currentNewName = topNewName.map(AbilityType.fullName(_, fName))
|
||||||
fValue match {
|
fValue match {
|
||||||
case nestedAbilityType @ AbilityType(_, _) =>
|
case nestedAbilityType @ AbilityType(_, _) =>
|
||||||
getVarsAndArrowsFromAbilities(
|
getVarsAndArrowsFromAbilities(
|
||||||
currentOldName,
|
currentOldName,
|
||||||
currentNewName,
|
currentNewName,
|
||||||
nestedAbilityType,
|
nestedAbilityType,
|
||||||
oldExports,
|
exports,
|
||||||
oldArrows,
|
arrows
|
||||||
valAcc,
|
|
||||||
arrowsAcc
|
|
||||||
)
|
)
|
||||||
case ArrowType(_, _) =>
|
case ArrowType(_, _) =>
|
||||||
oldExports
|
Exports
|
||||||
.get(currentOldName)
|
.getLastValue(currentOldName, exports)
|
||||||
.flatMap {
|
.flatMap { case vm @ VarModel(name, _, _) =>
|
||||||
case vm @ VarModel(name, _, _) =>
|
arrows
|
||||||
oldArrows
|
.get(name)
|
||||||
.get(name)
|
.map(fa =>
|
||||||
.map(fa =>
|
(
|
||||||
(
|
Map(currentNewName.getOrElse(currentOldName) -> vm),
|
||||||
valAcc.updated(currentNewName.getOrElse(currentOldName), vm),
|
Map(name -> fa)
|
||||||
arrowsAcc.updated(name, fa)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
case _ => None
|
)
|
||||||
}
|
}
|
||||||
.getOrElse((valAcc, arrowsAcc))
|
.getOrElse((Map.empty, Map.empty))
|
||||||
|
|
||||||
case _ =>
|
case _ =>
|
||||||
oldExports
|
Exports
|
||||||
.get(currentOldName)
|
.getLastValue(currentOldName, exports)
|
||||||
.map(vm => (valAcc.updated(currentNewName.getOrElse(currentOldName), vm), arrowsAcc))
|
.map(vm => (Map(currentNewName.getOrElse(currentOldName) -> vm), Map.empty))
|
||||||
.getOrElse((valAcc, arrowsAcc))
|
.getOrElse((Map.empty, Map.empty))
|
||||||
}
|
}
|
||||||
}.foldMapA(_.bimap(_.toList, _.toList)).bimap(_.toMap, _.toMap)
|
}.foldMapA(_.bimap(_.toList, _.toList)).bimap(_.toMap, _.toMap)
|
||||||
}
|
}
|
||||||
@ -352,7 +350,8 @@ object ArrowInliner extends Logging {
|
|||||||
private def prelude[S: Mangler: Arrows: Exports](
|
private def prelude[S: Mangler: Arrows: Exports](
|
||||||
fn: FuncArrow,
|
fn: FuncArrow,
|
||||||
call: CallModel,
|
call: CallModel,
|
||||||
oldExports: Map[String, ValueModel]
|
oldExports: Map[String, ValueModel],
|
||||||
|
arrows: Map[String, FuncArrow]
|
||||||
): State[S, (RawTag.Tree, List[ValueRaw])] =
|
): State[S, (RawTag.Tree, List[ValueRaw])] =
|
||||||
for {
|
for {
|
||||||
// Collect all arguments: what names are used inside the function, what values are received
|
// Collect all arguments: what names are used inside the function, what values are received
|
||||||
@ -360,24 +359,47 @@ object ArrowInliner extends Logging {
|
|||||||
|
|
||||||
abArgs = args.abilityArgs
|
abArgs = args.abilityArgs
|
||||||
|
|
||||||
// Going to resolve arrows: collect them all. Names should never collide: it's semantically checked
|
|
||||||
previousArrowsState <- Arrows[S].arrows
|
|
||||||
|
|
||||||
_ <- Arrows[S].purge
|
|
||||||
|
|
||||||
abilityResolvingResult <- abArgs.toList.traverse { case (str, (vm, sct)) =>
|
abilityResolvingResult <- abArgs.toList.traverse { case (str, (vm, sct)) =>
|
||||||
renameAndResolveAbilities(str, vm, sct, oldExports, previousArrowsState)
|
renameAndResolveAbilities(str, vm, sct, oldExports, arrows)
|
||||||
}
|
}.map(_.combineAll)
|
||||||
|
|
||||||
absRenames = abilityResolvingResult.map(_.namesToRename).fold(Map.empty)(_ ++ _)
|
absRenames = abilityResolvingResult.namesToRename
|
||||||
absVars = abilityResolvingResult.map(_.renamedExports).fold(Map.empty)(_ ++ _)
|
absVars = abilityResolvingResult.renamedExports
|
||||||
absArrows = abilityResolvingResult.map(_.renamedArrows).fold(Map.empty)(_ ++ _)
|
absArrows = abilityResolvingResult.renamedArrows
|
||||||
|
|
||||||
arrowArgs = args.arrowArgs(previousArrowsState)
|
arrowArgs = args.arrowArgs(arrows)
|
||||||
// Update states and rename tags
|
// Update states and rename tags
|
||||||
renamedArrows <- updateArrowsAndRenameArrowArgs(arrowArgs ++ absArrows, fn, absRenames)
|
renamedArrows <- updateArrowsAndRenameArrowArgs(arrowArgs ++ absArrows, fn, absRenames)
|
||||||
|
|
||||||
argsToDataShouldRename <- updateExportsAndRenameDataArgs(args.dataArgs ++ absVars, absRenames)
|
argsToDataShouldRename <- updateExportsAndRenameDataArgs(args.dataArgs ++ absVars, absRenames)
|
||||||
allShouldRename = argsToDataShouldRename ++ renamedArrows ++ absRenames
|
|
||||||
|
// rename variables that store arrows
|
||||||
|
_ <- Exports[S].renameVariables(renamedArrows)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Don't rename arrows from abilities in a body, because we link arrows by VarModel
|
||||||
|
* and they won't be called directly.
|
||||||
|
* They could intersect with arrows defined inside the body
|
||||||
|
*
|
||||||
|
* ability Simple:
|
||||||
|
* arrow() -> bool
|
||||||
|
*
|
||||||
|
* func foo{Simple}() -> bool, bool:
|
||||||
|
* closure = () -> bool:
|
||||||
|
* <- true
|
||||||
|
* <- closure(), Simple.arrow()
|
||||||
|
*
|
||||||
|
* func main() -> bool, bool:
|
||||||
|
* closure = () -> bool:
|
||||||
|
* <- false
|
||||||
|
* MySimple = Simple(arrow = closure)
|
||||||
|
* -- here we will rename arrow in Arrows[S] to 'closure-0'
|
||||||
|
* -- and link to arrow as 'Simple.arrow' -> VarModel('closure-0')
|
||||||
|
* -- and it will work well with closure with the same name 'closure' inside 'foo'
|
||||||
|
* foo{MySimple}()
|
||||||
|
*/
|
||||||
|
allShouldRename = argsToDataShouldRename ++ (renamedArrows -- absArrows.keySet) ++ absRenames
|
||||||
|
|
||||||
// Rename all renamed arguments in the body
|
// Rename all renamed arguments in the body
|
||||||
treeRenamed = fn.body.rename(allShouldRename)
|
treeRenamed = fn.body.rename(allShouldRename)
|
||||||
treeStreamsRenamed = renameStreams(treeRenamed, args.streamArgs)
|
treeStreamsRenamed = renameStreams(treeRenamed, args.streamArgs)
|
||||||
@ -415,8 +437,7 @@ object ArrowInliner extends Logging {
|
|||||||
} yield {
|
} yield {
|
||||||
sc.fields.toSortedMap.toList.flatMap {
|
sc.fields.toSortedMap.toList.flatMap {
|
||||||
case (n, ArrowType(_, _)) =>
|
case (n, ArrowType(_, _)) =>
|
||||||
val fullName = s"$name.$n"
|
exports.get(AbilityType.fullName(name, n)).flatMap {
|
||||||
exports.get(fullName).flatMap {
|
|
||||||
case VarModel(n, _, _) => arrows.get(n).map(n -> _)
|
case VarModel(n, _, _) => arrows.get(n).map(n -> _)
|
||||||
case _ => None
|
case _ => None
|
||||||
}
|
}
|
||||||
@ -435,11 +456,22 @@ object ArrowInliner extends Logging {
|
|||||||
.traverse(getAllArrowsFromAbility)
|
.traverse(getAllArrowsFromAbility)
|
||||||
.map(_.fold(Map.empty)(_ ++ _))
|
.map(_.fold(Map.empty)(_ ++ _))
|
||||||
|
|
||||||
inlineResult <- Arrows[S].scope(
|
exports <- Exports[S].exports
|
||||||
for {
|
streams <- getOutsideStreamNames
|
||||||
_ <- Arrows[S].resolved(passArrows ++ arrowsFromAbilities)
|
|
||||||
inlineResult <- ArrowInliner.inline(arrow, call)
|
inlineResult <- Exports[S].scope(
|
||||||
} yield inlineResult
|
Arrows[S].scope(
|
||||||
|
for {
|
||||||
|
// Process renamings, prepare environment
|
||||||
|
tr <- prelude[S](arrow, call, exports, passArrows ++ arrowsFromAbilities)
|
||||||
|
(tree, results) = tr
|
||||||
|
inlineResult <- ArrowInliner.inline(
|
||||||
|
arrow.copy(body = tree, ret = results),
|
||||||
|
call,
|
||||||
|
streams
|
||||||
|
)
|
||||||
|
} yield inlineResult
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
_ <- Arrows[S].resolved(inlineResult.arrowsToSave)
|
_ <- Arrows[S].resolved(inlineResult.arrowsToSave)
|
||||||
|
@ -329,19 +329,19 @@ object TagInliner extends Logging {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case AssignmentTag(value, assignTo) =>
|
case AssignmentTag(value, assignTo) =>
|
||||||
(value match {
|
for {
|
||||||
// if we assign collection to a stream, we must use it's name, because it is already created with 'new'
|
// NOTE: Name <assignTo> should not exist yet
|
||||||
case c @ CollectionRaw(_, _: StreamType) =>
|
_ <- Mangler[S].forbidName(assignTo)
|
||||||
collectionToModel(c, Some(assignTo))
|
modelAndPrefix <- value match {
|
||||||
case v =>
|
// if we assign collection to a stream, we must use it's name, because it is already created with 'new'
|
||||||
valueToModel(v, false)
|
case c @ CollectionRaw(_, _: StreamType) =>
|
||||||
}).flatMap { case (model, prefix) =>
|
collectionToModel(c, Some(assignTo))
|
||||||
for {
|
case v =>
|
||||||
// NOTE: Name <assignTo> should not exist yet
|
valueToModel(v, false)
|
||||||
_ <- Mangler[S].forbidName(assignTo)
|
}
|
||||||
_ <- Exports[S].resolved(assignTo, model)
|
(model, prefix) = modelAndPrefix
|
||||||
} yield TagInlined.Empty(prefix = prefix)
|
_ <- Exports[S].resolved(assignTo, model)
|
||||||
}
|
} yield TagInlined.Empty(prefix = prefix)
|
||||||
|
|
||||||
case ClosureTag(arrow, detach) =>
|
case ClosureTag(arrow, detach) =>
|
||||||
if (detach) Arrows[S].resolved(arrow, None).as(TagInlined.Empty())
|
if (detach) Arrows[S].resolved(arrow, None).as(TagInlined.Empty())
|
||||||
|
@ -1,62 +1,21 @@
|
|||||||
package aqua.model.inline.raw
|
package aqua.model.inline.raw
|
||||||
|
|
||||||
import aqua.model.{
|
import aqua.model.*
|
||||||
CallModel,
|
import aqua.model.ValueModel.Ability
|
||||||
CallServiceModel,
|
|
||||||
FlattenModel,
|
|
||||||
ForModel,
|
|
||||||
FunctorModel,
|
|
||||||
IntoFieldModel,
|
|
||||||
IntoIndexModel,
|
|
||||||
LiteralModel,
|
|
||||||
MatchMismatchModel,
|
|
||||||
NextModel,
|
|
||||||
OpModel,
|
|
||||||
PropertyModel,
|
|
||||||
PushToStreamModel,
|
|
||||||
SeqModel,
|
|
||||||
ValueModel,
|
|
||||||
VarModel,
|
|
||||||
XorModel
|
|
||||||
}
|
|
||||||
import aqua.model.inline.Inline
|
import aqua.model.inline.Inline
|
||||||
import aqua.model.inline.Inline.MergeMode.*
|
import aqua.model.inline.Inline.MergeMode.*
|
||||||
import aqua.model.inline.RawValueInliner.unfold
|
import aqua.model.inline.RawValueInliner.unfold
|
||||||
import aqua.model.inline.state.{Arrows, Exports, Mangler}
|
import aqua.model.inline.state.{Arrows, Exports, Mangler}
|
||||||
import aqua.raw.value.{
|
import aqua.raw.value.*
|
||||||
ApplyGateRaw,
|
import aqua.types.*
|
||||||
ApplyPropertyRaw,
|
|
||||||
CallArrowRaw,
|
|
||||||
FunctorRaw,
|
|
||||||
IntoArrowRaw,
|
|
||||||
IntoCopyRaw,
|
|
||||||
IntoFieldRaw,
|
|
||||||
IntoIndexRaw,
|
|
||||||
LiteralRaw,
|
|
||||||
PropertyRaw,
|
|
||||||
ValueRaw,
|
|
||||||
VarRaw
|
|
||||||
}
|
|
||||||
import aqua.types.{
|
|
||||||
AbilityType,
|
|
||||||
ArrayType,
|
|
||||||
ArrowType,
|
|
||||||
BottomType,
|
|
||||||
CanonStreamType,
|
|
||||||
NilType,
|
|
||||||
ScalarType,
|
|
||||||
StreamType,
|
|
||||||
Type
|
|
||||||
}
|
|
||||||
|
|
||||||
import cats.Eval
|
import cats.Eval
|
||||||
import cats.syntax.bifunctor.*
|
|
||||||
import cats.data.{Chain, IndexedStateT, State}
|
import cats.data.{Chain, IndexedStateT, State}
|
||||||
|
import cats.instances.list.*
|
||||||
|
import cats.syntax.applicative.*
|
||||||
|
import cats.syntax.bifunctor.*
|
||||||
|
import cats.syntax.foldable.*
|
||||||
import cats.syntax.monoid.*
|
import cats.syntax.monoid.*
|
||||||
import cats.syntax.traverse.*
|
import cats.syntax.traverse.*
|
||||||
import cats.syntax.foldable.*
|
|
||||||
import cats.Id
|
|
||||||
import cats.instances.list.*
|
|
||||||
import scribe.Logging
|
import scribe.Logging
|
||||||
|
|
||||||
object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Logging {
|
object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Logging {
|
||||||
@ -100,12 +59,12 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi
|
|||||||
|
|
||||||
private def unfoldAbilityProperty[S: Mangler: Exports: Arrows](
|
private def unfoldAbilityProperty[S: Mangler: Exports: Arrows](
|
||||||
varModel: VarModel,
|
varModel: VarModel,
|
||||||
scopeType: AbilityType,
|
abilityType: AbilityType,
|
||||||
p: PropertyRaw
|
p: PropertyRaw
|
||||||
): State[S, (VarModel, Inline)] = {
|
): State[S, (VarModel, Inline)] = {
|
||||||
p match {
|
p match {
|
||||||
case IntoArrowRaw(arrowName, t, arguments) =>
|
case IntoArrowRaw(arrowName, t, arguments) =>
|
||||||
val arrowType = scopeType.fields
|
val arrowType = abilityType.fields
|
||||||
.lookup(arrowName)
|
.lookup(arrowName)
|
||||||
.collect { case at @ ArrowType(_, _) =>
|
.collect { case at @ ArrowType(_, _) =>
|
||||||
at
|
at
|
||||||
@ -116,7 +75,13 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi
|
|||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
callArrow <- CallArrowRawInliner(
|
callArrow <- CallArrowRawInliner(
|
||||||
CallArrowRaw(None, s"${varModel.name}.$arrowName", arguments, arrowType, None)
|
CallArrowRaw(
|
||||||
|
None,
|
||||||
|
AbilityType.fullName(varModel.name, arrowName),
|
||||||
|
arguments,
|
||||||
|
arrowType,
|
||||||
|
None
|
||||||
|
)
|
||||||
)
|
)
|
||||||
result <- callArrow match {
|
result <- callArrow match {
|
||||||
case (vm: VarModel, inl) =>
|
case (vm: VarModel, inl) =>
|
||||||
@ -129,21 +94,25 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi
|
|||||||
} yield {
|
} yield {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
case IntoFieldRaw(fieldName, at @ AbilityType(abName, fields)) =>
|
||||||
|
(VarModel(AbilityType.fullName(varModel.name, fieldName), at), Inline.empty).pure
|
||||||
case IntoFieldRaw(fieldName, t) =>
|
case IntoFieldRaw(fieldName, t) =>
|
||||||
for {
|
for {
|
||||||
exports <- Exports[S].exports
|
abilityField <- Exports[S].getAbilityField(varModel.name, fieldName)
|
||||||
fullName = s"${varModel.name}.$fieldName"
|
result <- abilityField match {
|
||||||
result <- exports.get(fullName) match {
|
|
||||||
case Some(vm: VarModel) =>
|
case Some(vm: VarModel) =>
|
||||||
State.pure((vm, Inline.empty))
|
State.pure((vm, Inline.empty))
|
||||||
case Some(lm: LiteralModel) =>
|
case Some(lm: LiteralModel) =>
|
||||||
flatLiteralWithProperties(lm, Inline.empty, Chain.empty)
|
flatLiteralWithProperties(lm, Inline.empty, Chain.empty)
|
||||||
case _ =>
|
case _ =>
|
||||||
logger.error(
|
Exports[S].getKeys.flatMap { keys =>
|
||||||
s"Inlining, cannot find field $fullName in ability $varModel. Available: ${exports.keySet}"
|
logger.error(
|
||||||
)
|
s"Inlining, cannot find field ${AbilityType
|
||||||
flatLiteralWithProperties(LiteralModel.quote(""), Inline.empty, Chain.empty)
|
.fullName(varModel.name, fieldName)} in ability $varModel. Available: $keys"
|
||||||
|
)
|
||||||
|
flatLiteralWithProperties(LiteralModel.quote(""), Inline.empty, Chain.empty)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
} yield {
|
} yield {
|
||||||
result
|
result
|
||||||
@ -246,8 +215,8 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi
|
|||||||
State.pure((vm, prevInline.mergeWith(optimizationInline, SeqMode)))
|
State.pure((vm, prevInline.mergeWith(optimizationInline, SeqMode)))
|
||||||
) { case (state, property) =>
|
) { case (state, property) =>
|
||||||
state.flatMap {
|
state.flatMap {
|
||||||
case (vm @ VarModel(name, st @ AbilityType(_, _), _), leftInline) =>
|
case (vm @ Ability(name, at, _), leftInline) =>
|
||||||
unfoldAbilityProperty(vm, st, property.raw).map { case (vm, inl) =>
|
unfoldAbilityProperty(vm, at, property.raw).map { case (vm, inl) =>
|
||||||
(
|
(
|
||||||
vm,
|
vm,
|
||||||
Inline(
|
Inline(
|
||||||
|
@ -76,16 +76,12 @@ object CallArrowRawInliner extends RawInliner[CallArrowRaw] with Logging {
|
|||||||
): State[S, (List[ValueModel], Inline)] = for {
|
): State[S, (List[ValueModel], Inline)] = for {
|
||||||
arrows <- Arrows[S].arrows
|
arrows <- Arrows[S].arrows
|
||||||
exports <- Exports[S].exports
|
exports <- Exports[S].exports
|
||||||
|
lastArrow <- Exports[S].getLast(funcName)
|
||||||
arrow = arrows
|
arrow = arrows
|
||||||
.get(funcName)
|
.get(funcName)
|
||||||
.orElse(
|
.orElse(
|
||||||
// if there is no arrow, check if it is stored in Exports as variable and try to resolve it
|
// if there is no arrow, check if it is stored in Exports as variable and try to resolve it
|
||||||
exports
|
lastArrow.flatMap(arrows.get)
|
||||||
.get(funcName)
|
|
||||||
.collect { case VarModel(name, _: ArrowType, _) =>
|
|
||||||
name
|
|
||||||
}
|
|
||||||
.flatMap(arrows.get)
|
|
||||||
)
|
)
|
||||||
result <- arrow.fold {
|
result <- arrow.fold {
|
||||||
logger.error(
|
logger.error(
|
||||||
|
@ -1,31 +1,33 @@
|
|||||||
package aqua.model.inline.raw
|
package aqua.model.inline.raw
|
||||||
|
|
||||||
import aqua.model.{
|
|
||||||
CallModel,
|
|
||||||
CallServiceModel,
|
|
||||||
LiteralModel,
|
|
||||||
OpModel,
|
|
||||||
SeqModel,
|
|
||||||
ValueModel,
|
|
||||||
VarModel
|
|
||||||
}
|
|
||||||
import aqua.model.inline.raw.RawInliner
|
|
||||||
import cats.data.Chain
|
|
||||||
import aqua.model.inline.state.{Arrows, Exports, Mangler}
|
|
||||||
import aqua.raw.value.{AbilityRaw, LiteralRaw, MakeStructRaw}
|
|
||||||
import cats.data.{NonEmptyList, NonEmptyMap, State}
|
|
||||||
import aqua.model.inline.Inline
|
import aqua.model.inline.Inline
|
||||||
import aqua.model.inline.RawValueInliner.{unfold, valueToModel}
|
import aqua.model.inline.RawValueInliner.unfold
|
||||||
import aqua.types.{ArrowType, ScalarType}
|
import aqua.model.inline.state.{Arrows, Exports, Mangler}
|
||||||
import cats.syntax.traverse.*
|
import aqua.model.{SeqModel, ValueModel, VarModel}
|
||||||
import cats.syntax.monoid.*
|
import aqua.raw.value.AbilityRaw
|
||||||
import cats.syntax.functor.*
|
import aqua.types.AbilityType
|
||||||
import cats.syntax.flatMap.*
|
import aqua.model.ValueModel.Ability
|
||||||
import cats.syntax.apply.*
|
import cats.Eval
|
||||||
|
import cats.data.{Chain, IndexedStateT, NonEmptyMap, State}
|
||||||
import cats.syntax.foldable.*
|
import cats.syntax.foldable.*
|
||||||
|
|
||||||
object MakeAbilityRawInliner extends RawInliner[AbilityRaw] {
|
object MakeAbilityRawInliner extends RawInliner[AbilityRaw] {
|
||||||
|
|
||||||
|
private def updateFields[S: Mangler: Exports: Arrows](
|
||||||
|
name: String,
|
||||||
|
fields: NonEmptyMap[String, (ValueModel, Inline)]
|
||||||
|
): State[S, Unit] = {
|
||||||
|
for {
|
||||||
|
res <- fields.toNel.traverse {
|
||||||
|
case (n, (Ability(abilityName, _, _), _)) =>
|
||||||
|
val leftName = AbilityType.fullName(name, n)
|
||||||
|
Exports[S].copyWithAbilityPrefix(abilityName, leftName)
|
||||||
|
case (n, (vm, _)) =>
|
||||||
|
Exports[S].resolveAbilityField(name, n, vm)
|
||||||
|
}
|
||||||
|
} yield ()
|
||||||
|
}
|
||||||
|
|
||||||
override def apply[S: Mangler: Exports: Arrows](
|
override def apply[S: Mangler: Exports: Arrows](
|
||||||
raw: AbilityRaw,
|
raw: AbilityRaw,
|
||||||
propertiesAllowed: Boolean
|
propertiesAllowed: Boolean
|
||||||
@ -35,9 +37,7 @@ object MakeAbilityRawInliner extends RawInliner[AbilityRaw] {
|
|||||||
foldedFields <- raw.fieldsAndArrows.nonEmptyTraverse(unfold(_))
|
foldedFields <- raw.fieldsAndArrows.nonEmptyTraverse(unfold(_))
|
||||||
varModel = VarModel(name, raw.baseType)
|
varModel = VarModel(name, raw.baseType)
|
||||||
valsInline = foldedFields.toList.foldMap { case (_, inline) => inline }.desugar
|
valsInline = foldedFields.toList.foldMap { case (_, inline) => inline }.desugar
|
||||||
_ <- foldedFields.toNel.traverse { case (n, (vm, _)) =>
|
_ <- updateFields(name, foldedFields)
|
||||||
Exports[S].resolved(s"$name.$n", vm)
|
|
||||||
}
|
|
||||||
} yield {
|
} yield {
|
||||||
(
|
(
|
||||||
varModel,
|
varModel,
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package aqua.model.inline.state
|
package aqua.model.inline.state
|
||||||
|
|
||||||
import aqua.model.ValueModel
|
import aqua.model.{ValueModel, VarModel}
|
||||||
import aqua.raw.ops.Call
|
import aqua.model.ValueModel.Ability
|
||||||
import aqua.raw.value.ValueRaw
|
import aqua.types.AbilityType
|
||||||
import cats.data.State
|
import cats.data.{NonEmptyList, State}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exports – trace values available in the scope
|
* Exports – trace values available in the scope
|
||||||
@ -22,6 +22,37 @@ trait Exports[S] extends Scoped[S] {
|
|||||||
*/
|
*/
|
||||||
def resolved(exportName: String, value: ValueModel): State[S, Unit]
|
def resolved(exportName: String, value: ValueModel): State[S, Unit]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [[value]] is accessible as [[abilityExportName]].[[fieldName]]
|
||||||
|
*
|
||||||
|
* @param abilityExportName
|
||||||
|
* Ability Name
|
||||||
|
* @param fieldName
|
||||||
|
* Field Name
|
||||||
|
* @param value
|
||||||
|
* Value
|
||||||
|
*/
|
||||||
|
def resolveAbilityField(
|
||||||
|
abilityExportName: String,
|
||||||
|
fieldName: String,
|
||||||
|
value: ValueModel
|
||||||
|
): State[S, Unit]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rename ability prefix to new one
|
||||||
|
*/
|
||||||
|
def copyWithAbilityPrefix(prefix: String, newPrefix: String): State[S, Unit]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get name of last linked VarModel
|
||||||
|
*/
|
||||||
|
def getLast(name: String): State[S, Option[String]]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rename names in variables
|
||||||
|
*/
|
||||||
|
def renameVariables(renames: Map[String, String]): State[S, Unit]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve the whole map of exports
|
* Resolve the whole map of exports
|
||||||
* @param exports
|
* @param exports
|
||||||
@ -29,6 +60,18 @@ trait Exports[S] extends Scoped[S] {
|
|||||||
*/
|
*/
|
||||||
def resolved(exports: Map[String, ValueModel]): State[S, Unit]
|
def resolved(exports: Map[String, ValueModel]): State[S, Unit]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all export keys
|
||||||
|
*/
|
||||||
|
def getKeys: State[S, Set[String]]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get ability field from export
|
||||||
|
* @param name variable ability name
|
||||||
|
* @param field ability field
|
||||||
|
*/
|
||||||
|
def getAbilityField(name: String, field: String): State[S, Option[ValueModel]]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all the values available in the scope
|
* Get all the values available in the scope
|
||||||
*/
|
*/
|
||||||
@ -45,6 +88,28 @@ trait Exports[S] extends Scoped[S] {
|
|||||||
override def resolved(exports: Map[String, ValueModel]): State[R, Unit] =
|
override def resolved(exports: Map[String, ValueModel]): State[R, Unit] =
|
||||||
self.resolved(exports).transformS(f, g)
|
self.resolved(exports).transformS(f, g)
|
||||||
|
|
||||||
|
override def resolveAbilityField(
|
||||||
|
abilityExportName: String,
|
||||||
|
fieldName: String,
|
||||||
|
value: ValueModel
|
||||||
|
): State[R, Unit] =
|
||||||
|
self.resolveAbilityField(abilityExportName, fieldName, value).transformS(f, g)
|
||||||
|
|
||||||
|
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 renameVariables(renames: Map[String, String]): State[R, Unit] =
|
||||||
|
self.renameVariables(renames).transformS(f, g)
|
||||||
|
|
||||||
|
override def getKeys: State[R, Set[String]] =
|
||||||
|
self.getKeys.transformS(f, g)
|
||||||
|
|
||||||
|
override def getAbilityField(name: String, field: String): State[R, Option[ValueModel]] =
|
||||||
|
self.getAbilityField(name, field).transformS(f, g)
|
||||||
|
|
||||||
override val exports: State[R, Map[String, ValueModel]] =
|
override val exports: State[R, Map[String, ValueModel]] =
|
||||||
self.exports.transformS(f, g)
|
self.exports.transformS(f, g)
|
||||||
|
|
||||||
@ -59,18 +124,91 @@ trait Exports[S] extends Scoped[S] {
|
|||||||
object Exports {
|
object Exports {
|
||||||
def apply[S](implicit exports: Exports[S]): Exports[S] = 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] = {
|
||||||
|
state.get(name) match {
|
||||||
|
case Some(vm@VarModel(n, _, _)) =>
|
||||||
|
if (name == n) Option(vm)
|
||||||
|
else getLastValue(n, state).orElse(Option(vm))
|
||||||
|
case n =>
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
object Simple extends Exports[Map[String, ValueModel]] {
|
object Simple extends Exports[Map[String, ValueModel]] {
|
||||||
|
|
||||||
// Exports[Map[NonEmptyList[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)] = {
|
||||||
|
at.fields.toNel.flatMap {
|
||||||
|
case (n, at@AbilityType(_, _)) =>
|
||||||
|
val newFullName = AbilityType.fullName(newName, n)
|
||||||
|
val oldFullName = AbilityType.fullName(oldName, n)
|
||||||
|
getAbilityPairs(oldFullName, newFullName, at, state)
|
||||||
|
case (n, t) =>
|
||||||
|
val newFullName = AbilityType.fullName(newName, n)
|
||||||
|
val oldFullName = AbilityType.fullName(oldName, n)
|
||||||
|
// put link on last variable in chain
|
||||||
|
val lastVar = Exports.getLastValue(oldFullName, state)
|
||||||
|
NonEmptyList.of((newFullName, lastVar.getOrElse(VarModel(oldFullName, t))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override def resolved(
|
override def resolved(
|
||||||
exportName: String,
|
exportName: String,
|
||||||
value: ValueModel
|
value: ValueModel
|
||||||
): State[Map[String, ValueModel], Unit] = State.modify(_ + (exportName -> value))
|
): State[Map[String, ValueModel], Unit] = State.modify { state =>
|
||||||
|
value match {
|
||||||
|
case vm@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 resolved(exports: Map[String, ValueModel]): State[Map[String, ValueModel], Unit] =
|
override def resolved(exports: Map[String, ValueModel]): State[Map[String, ValueModel], Unit] =
|
||||||
State.modify(_ ++ exports)
|
State.modify(_ ++ exports)
|
||||||
|
|
||||||
|
override def resolveAbilityField(
|
||||||
|
abilityExportName: String,
|
||||||
|
fieldName: String,
|
||||||
|
value: ValueModel
|
||||||
|
): State[Map[String, ValueModel], Unit] =
|
||||||
|
State.modify(_ + (AbilityType.fullName(abilityExportName, fieldName) -> value))
|
||||||
|
|
||||||
|
override def copyWithAbilityPrefix(
|
||||||
|
prefix: String,
|
||||||
|
newPrefix: String
|
||||||
|
): State[Map[String, ValueModel], Unit] =
|
||||||
|
State.modify { state =>
|
||||||
|
state.flatMap {
|
||||||
|
case (k, v) if k.startsWith(prefix) =>
|
||||||
|
List(k.replaceFirst(prefix, newPrefix) -> v, k -> v)
|
||||||
|
case (k, v) => List(k -> v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override def renameVariables(
|
||||||
|
renames: Map[String, String]
|
||||||
|
): State[Map[String, ValueModel], Unit] =
|
||||||
|
State.modify {
|
||||||
|
_.map {
|
||||||
|
case (k, vm @ VarModel(name, _, _)) if renames.contains(name) =>
|
||||||
|
k -> vm.copy(name = renames.getOrElse(name, name))
|
||||||
|
case (k, v) => k -> v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override def getKeys: State[Map[String, ValueModel], Set[String]] = State.get.map(_.keySet)
|
||||||
|
|
||||||
|
override def getAbilityField(
|
||||||
|
name: String,
|
||||||
|
field: String
|
||||||
|
): State[Map[String, ValueModel], Option[ValueModel]] =
|
||||||
|
State.get.map(_.get(AbilityType.fullName(name, field)))
|
||||||
|
|
||||||
override val exports: State[Map[String, ValueModel], Map[String, ValueModel]] =
|
override val exports: State[Map[String, ValueModel], Map[String, ValueModel]] =
|
||||||
State.get
|
State.get
|
||||||
|
|
||||||
|
@ -51,6 +51,15 @@ object ValueModel {
|
|||||||
case _ => ???
|
case _ => ???
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object Ability {
|
||||||
|
|
||||||
|
def unapply(vm: VarModel): Option[(String, AbilityType, Chain[PropertyModel])] =
|
||||||
|
vm match {
|
||||||
|
case VarModel(name, t@AbilityType(_, _), properties) =>
|
||||||
|
(name, t, properties).some
|
||||||
|
case _ => none
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case class LiteralModel(value: String, `type`: Type) extends ValueModel {
|
case class LiteralModel(value: String, `type`: Type) extends ValueModel {
|
||||||
|
@ -66,6 +66,7 @@ object OptionTypeToken {
|
|||||||
|
|
||||||
case class NamedTypeToken[F[_]: Comonad](name: F[String]) extends DataTypeToken[F] {
|
case class NamedTypeToken[F[_]: Comonad](name: F[String]) extends DataTypeToken[F] {
|
||||||
override def as[T](v: T): F[T] = name.as(v)
|
override def as[T](v: T): F[T] = name.as(v)
|
||||||
|
def asName: Name[F] = Name[F](name)
|
||||||
|
|
||||||
override def mapK[K[_]: Comonad](fk: F ~> K): NamedTypeToken[K] = copy(fk(name))
|
override def mapK[K[_]: Comonad](fk: F ~> K): NamedTypeToken[K] = copy(fk(name))
|
||||||
|
|
||||||
|
@ -36,9 +36,9 @@ class AbilitySem[S[_]](val expr: AbilityExpr[S]) extends AnyVal {
|
|||||||
t
|
t
|
||||||
): Raw
|
): Raw
|
||||||
case false =>
|
case false =>
|
||||||
Raw.error("Scope types unresolved")
|
Raw.error("Ability types unresolved")
|
||||||
}
|
}
|
||||||
case None => Raw.error("Scope types unresolved").pure[Alg]
|
case None => Raw.error("Ability types unresolved").pure[Alg]
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -300,6 +300,7 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](implicit
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate CallArrowRaw for arrow in ability
|
// Generate CallArrowRaw for arrow in ability
|
||||||
|
// WARNING: arguments are resolved at the end of the function and added to CallArrowRaw
|
||||||
def callAbType(
|
def callAbType(
|
||||||
ab: String,
|
ab: String,
|
||||||
abType: AbilityType,
|
abType: AbilityType,
|
||||||
@ -307,7 +308,7 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](implicit
|
|||||||
): Alg[Option[CallArrowRaw]] =
|
): Alg[Option[CallArrowRaw]] =
|
||||||
abType.arrows.get(ca.funcName.value) match {
|
abType.arrows.get(ca.funcName.value) match {
|
||||||
case Some(arrowType) =>
|
case Some(arrowType) =>
|
||||||
Option(CallArrowRaw(None, s"$ab.${ca.funcName.value}", Nil, arrowType, None)).pure[Alg]
|
Option(CallArrowRaw(None, AbilityType.fullName(ab, ca.funcName.value), Nil, arrowType, None)).pure[Alg]
|
||||||
case None => None.pure[Alg]
|
case None => None.pure[Alg]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,32 +329,38 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](implicit
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)(ab =>
|
)(ab =>
|
||||||
// TODO: Hack. Check that we have registered ability type.
|
// Check that we have variable as ability
|
||||||
// If it exists - this is ability type in file, if not - imported ability
|
N.read(ab.asName, false).flatMap {
|
||||||
T.getType(ab.value).flatMap {
|
case Some(at@AbilityType(_, _)) =>
|
||||||
case Some(abType: AbilityType) =>
|
callAbType(ab.value, at, ca)
|
||||||
callAbType(ab.value, abType, ca)
|
|
||||||
case _ =>
|
case _ =>
|
||||||
(A.getArrow(ab, ca.funcName), A.getServiceId(ab)).mapN {
|
// Check that we have registered ability type.
|
||||||
case (Some(at), Right(sid)) =>
|
// If it exists - this is ability type in file, if not - imported ability
|
||||||
// Service call, actually
|
T.getType(ab.value).flatMap {
|
||||||
CallArrowRaw(
|
case Some(abType: AbilityType) =>
|
||||||
ability = Some(ab.value),
|
callAbType(ab.value, abType, ca)
|
||||||
name = ca.funcName.value,
|
case t =>
|
||||||
arguments = Nil,
|
(A.getArrow(ab, ca.funcName), A.getServiceId(ab)).mapN {
|
||||||
baseType = at,
|
case (Some(at), Right(sid)) =>
|
||||||
serviceId = Some(sid)
|
// Service call, actually
|
||||||
).some
|
CallArrowRaw(
|
||||||
case (Some(at), Left(true)) =>
|
ability = Some(ab.value),
|
||||||
// Ability function call, actually
|
name = ca.funcName.value,
|
||||||
CallArrowRaw(
|
arguments = Nil,
|
||||||
ability = Some(ab.value),
|
baseType = at,
|
||||||
name = ca.funcName.value,
|
serviceId = Some(sid)
|
||||||
arguments = Nil,
|
).some
|
||||||
baseType = at,
|
case (Some(at), Left(true)) =>
|
||||||
serviceId = None
|
// Ability function call, actually
|
||||||
).some
|
CallArrowRaw(
|
||||||
case _ => none
|
ability = Some(ab.value),
|
||||||
|
name = ca.funcName.value,
|
||||||
|
arguments = Nil,
|
||||||
|
baseType = at,
|
||||||
|
serviceId = None
|
||||||
|
).some
|
||||||
|
case _ => none
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -267,7 +267,11 @@ case class AbilityType(name: String, fields: NonEmptyMap[String, Type]) extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
override def toString: String =
|
override def toString: String =
|
||||||
s"scope $name{${fields.map(_.toString).toNel.toList.map(kv => kv._1 + ": " + kv._2).mkString(", ")}}"
|
s"ability $name{${fields.map(_.toString).toNel.toList.map(kv => kv._1 + ": " + kv._2).mkString(", ")}}"
|
||||||
|
}
|
||||||
|
|
||||||
|
object AbilityType {
|
||||||
|
def fullName(name: String, field: String) = s"$name.$field"
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user