fix(compiler): Create restrictions in inliner [LNG-346] (#1099)

This commit is contained in:
Dima 2024-03-28 14:10:13 +03:00 committed by GitHub
parent 320b516807
commit 14748c7646
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
44 changed files with 706 additions and 632 deletions

View File

@ -128,7 +128,7 @@ class CallPreparer(
FuncArrow(
config.functionWrapperName,
SeqTag.wrap((getters.map(_.leaf) :+ body :+ finisherService.leaf): _*),
SeqTag.wrap(getters.map(_.leaf) :+ body :+ finisherService.leaf*),
// no arguments and returns "ok" string
ArrowType(NilType, returnCodomain),
ret,

View File

@ -1,15 +1,39 @@
aqua Main
aqua A
export main
export streamTry, streamFor
service Srv("srv"):
call(x: i32) -> i32
service FailureSrv("failure"):
fail(msg: string)
func main(a: i32, b: i32) -> i32:
res: *i32
if a > b:
on "peer" via "relay":
res <- Srv.call(a)
func streamTry() -> i8:
on HOST_PEER_ID:
try:
stream: *i8
anotherStream = stream
stream <<- 1
anotherStream <<- 1
FailureSrv.fail("try")
catch e:
stream = *[88,88,88]
stream <<- 2
FailureSrv.fail("catch")
otherwise:
stream: *i8
stream <<- 3
<- res!
stream: *i8
stream <<- 4
<- stream!
service StreamService("test-service"):
store(numbers: []u32, n: u32)
func callService(stream: *u32, n: u32):
stream <<- 1
StreamService.store(stream, n)
func streamFor():
arr = [1,2,3,4,5]
for a <- arr:
callService(*[], a)

View File

@ -41,11 +41,11 @@ func checkKeepArg() -> []string, []string:
<- a, y
-- failing Aqua code:
service TestService("test-service"):
service TestServiceRestrict("test-service"):
get_records(key: string) -> []string
func append_records(peer: string, srum: *[]string):
srum <- TestService.get_records(peer)
srum <- TestServiceRestrict.get_records(peer)
func retrieve_records(peer: string) -> [][]string:
records: *[]string

View File

@ -5,11 +5,11 @@ func some_string() -> string:
<- "some_string_func"
service TestService("test-service"):
service TestServiceRename("test-service"):
get_records(key: string) -> []string
func append_records(peer: string, srum: *[]string):
srum <- TestService.get_records(peer)
srum <- TestServiceRename.get_records(peer)
func retrieve_records(peer: string) -> [][]string:
records: *[]string

View File

@ -169,11 +169,10 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers with Inside {
val expected =
XorRes.wrap(
RestrictionRes("results", resultsType).wrap(
SeqRes.wrap(
getDataSrv("-relay-", "-relay-", ScalarType.string),
getDataSrv("peers", peers.name, peers.`type`),
RestrictionRes(results.name, resultsType).wrap(
SeqRes.wrap(
ParRes.wrap(
FoldRes
.lastNever(peer.name, peers)
@ -215,10 +214,9 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers with Inside {
ApRes(
canonResult,
CallModel.Export(flatResult.name, flatResult.`type`)
).leaf
)
),
).leaf,
respCall(transformCfg, flatResult, initPeer)
)
),
errorCall(transformCfg, 0, initPeer)
)
@ -352,8 +350,7 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers with Inside {
val resFlatVM = VarModel("-res-flat-0", ArrayType(ScalarType.string))
val expected = XorRes.wrap(
SeqRes.wrap(
RestrictionRes(resVM.name, resStreamType).wrap(
RestrictionRes(resVM.name, resVM.`type`).wrap(
SeqRes.wrap(
// res <- foo()
ApRes(
@ -375,10 +372,9 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers with Inside {
ApRes(
VarModel(resCanonVM.name, resCanonVM.`type`),
CallModel.Export(resFlatVM.name, resFlatVM.`type`)
).leaf
)
),
).leaf,
respCall(transformCfg, resFlatVM, initPeer)
)
),
errorCall(transformCfg, 0, initPeer)
)
@ -424,18 +420,16 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers with Inside {
).leaf
val expected = XorRes.wrap(
RestrictionRes(streamName, streamType).wrap(
SeqRes.wrap(
getDataSrv("-relay-", "-relay-", ScalarType.string),
getDataSrv("i", argName, argType),
RestrictionRes(streamName, streamType).wrap(
SeqRes.wrap(
ApRes(LiteralModel.quote("a"), CallModel.Export(streamName, streamType)).leaf,
ApRes(LiteralModel.quote("b"), CallModel.Export(streamName, streamType)).leaf,
join(VarModel(streamName, streamType), arg),
decrement
)
),
decrement,
emptyRespCall(transformCfg, initPeer)
)
),
errorCall(transformCfg, 0, initPeer)
)

View File

@ -1,18 +1,18 @@
aqua Test
export test, TestService
export test, TestServiceClosureArrowCapture
service TestService:
service TestServiceClosureArrowCapture:
call(s: string) -> string
ability TestAbility:
arrow(s: string) -> string
func returnCapture() -> string -> string:
TestService "test-service"
TestServiceClosureArrowCapture "test-service"
closure = (s: string) -> string:
<- TestService.call(s)
<- TestServiceClosureArrowCapture.call(s)
closure1 = closure
closure2 = closure1
@ -26,7 +26,7 @@ func returnCapture() -> string -> string:
s1 <- closure(s) -- capture closure
s2 <- closure3(s1) -- capture renamed closure
s3 <- Ab.arrow(s2) -- capture ability
s4 <- TestService.call(s3) -- capture service
s4 <- TestServiceClosureArrowCapture.call(s3) -- capture service
<- s4
<- capture

View File

@ -0,0 +1,49 @@
aqua ClosureStreamScopes
export simpleTest, complexTest
ability Join:
join1() -> []string
ability Fork:
fork() -> Join
func simpleTest() -> []string:
fork = () -> Join:
in: *string
join1 = () -> []string:
in <<- "something in nested"
<- ["result"]
in <<- "something in"
<- Join(join1)
f = Fork(fork)
j = f.fork()
<- j.join1()
func fork() -> Join:
in: *string
out: *string
join1 = () -> []string:
inJoin: *string
for i <- in rec:
inJoin <<- i
for o <- out rec:
inJoin <<- o
join in!
par join out!
<- inJoin
in <<- "something in INSIDE"
out <<- "something out INSIDE"
<- Join(join1)
func complexTest() -> []string, []string:
out: *string
f = Fork(fork = fork)
j <- f.fork()
strs <- j.join1()
out <<- "something out OUTSIDE"
<- strs, out

View File

@ -1,8 +1,8 @@
aqua MultiRec
export TestService, multiRecStream
export TestServiceMultiRec, multiRecStream
service TestService("test-srv"):
service TestServiceMultiRec("test-srv"):
handle(i: i32) -> []i32
func multiRecStream(init: i32, target: i32) -> []i32:
@ -11,7 +11,7 @@ func multiRecStream(init: i32, target: i32) -> []i32:
loop <<- init
for l <- loop rec:
news <- TestService.handle(l)
news <- TestServiceMultiRec.handle(l)
for n <- news:
loop <<- n
if l == target:

View File

@ -1,8 +1,8 @@
aqua Test
export test, testCapture, TestService
export test, testCapture, TestServiceAsAbility
service TestService("default-id"):
service TestServiceAsAbility("default-id"):
getId() -> string
concatId(s: string) -> string
@ -20,14 +20,14 @@ func test() -> []string:
result: *string
-- Test service
result <- TestService.concatId("call")
capture = TestService.concatId
result <- TestServiceAsAbility.concatId("call")
capture = TestServiceAsAbility.concatId
result <- capture("capture call")
result <- acceptClosure(TestService.concatId, "accept closure call")
result <- acceptAbility{TestService}("accept ability call")
result <- acceptClosure(TestServiceAsAbility.concatId, "accept closure call")
result <- acceptAbility{TestServiceAsAbility}("accept ability call")
-- Test renamed service
Renamed = TestService
Renamed = TestServiceAsAbility
result <- Renamed.concatId("call")
captureRenamed = Renamed.concatId
result <- captureRenamed("capture call")
@ -35,15 +35,15 @@ func test() -> []string:
result <- acceptAbility{Renamed}("accept ability call")
-- Test resolved service
TestService "resolved-id-1"
result <- TestService.concatId("call")
captureResolved = TestService.concatId
TestServiceAsAbility "resolved-id-1"
result <- TestServiceAsAbility.concatId("call")
captureResolved = TestServiceAsAbility.concatId
result <- captureResolved("capture call")
result <- acceptClosure(TestService.concatId, "accept closure call")
result <- acceptAbility{TestService}("accept ability call")
result <- acceptClosure(TestServiceAsAbility.concatId, "accept closure call")
result <- acceptAbility{TestServiceAsAbility}("accept ability call")
-- Test renamed resolved service
Renamed1 = TestService
Renamed1 = TestServiceAsAbility
result <- Renamed1.concatId("call")
captureRenamed1 = Renamed1.concatId
result <- captureRenamed1("capture call")
@ -59,8 +59,8 @@ func test() -> []string:
-- Test resolved in scope service
for i <- ["iter-id-1", "iter-id-2"]:
TestService i
RenamedI = TestService
TestServiceAsAbility i
RenamedI = TestServiceAsAbility
result <- RenamedI.concatId("call")
captureI = RenamedI.concatId
result <- captureI("capture call")
@ -68,28 +68,28 @@ func test() -> []string:
result <- acceptAbility{RenamedI}("accept ability call")
-- Test resolved service again (should save id)
result <- TestService.concatId("call")
captureAgain = TestService.concatId
result <- TestServiceAsAbility.concatId("call")
captureAgain = TestServiceAsAbility.concatId
result <- captureAgain("capture call")
result <- acceptClosure(TestService.concatId, "accept closure call")
result <- acceptAbility{TestService}("accept ability call")
result <- acceptClosure(TestServiceAsAbility.concatId, "accept closure call")
result <- acceptAbility{TestServiceAsAbility}("accept ability call")
-- Test re resolved service in same scope
TestService "resolved-id-2"
result <- TestService.concatId("call")
captureReResolved = TestService.concatId
TestServiceAsAbility "resolved-id-2"
result <- TestServiceAsAbility.concatId("call")
captureReResolved = TestServiceAsAbility.concatId
result <- captureReResolved("capture call")
result <- acceptClosure(TestService.concatId, "accept closure call")
result <- acceptAbility{TestService}("accept ability call")
result <- acceptClosure(TestServiceAsAbility.concatId, "accept closure call")
result <- acceptAbility{TestServiceAsAbility}("accept ability call")
<- result
func callCapture{MatchingAbility}() -> string, string:
TestService "resolved-id-in-capture"
res1 <- TestService.concatId("in capture")
TestServiceAsAbility "resolved-id-in-capture"
res1 <- TestServiceAsAbility.concatId("in capture")
res2 <- MatchingAbility.concatId("in capture")
<- res1, res2
func testCapture() -> string, string:
res1, res2 <- callCapture{TestService}()
res1, res2 <- callCapture{TestServiceAsAbility}()
<- res1, res2

View File

@ -23,6 +23,7 @@ import { registerPrintln } from "../compiled/examples/println.js";
import { helloWorldCall } from "../examples/helloWorldCall.js";
import { foldBug499Call, foldCall } from "../examples/foldCall.js";
import { bugNG69Call, ifCall, ifWrapCall } from "../examples/ifCall.js";
import { simpleStreamScopeCall, complexStreamScopeCall } from "../examples/closureStreamScopesCall.js";
import { ifPropagateErrorsCall } from "../examples/ifPropagateErrors.js";
import { parCall, testTimeoutCall } from "../examples/parCall.js";
import { complexCall } from "../examples/complex.js";
@ -767,6 +768,17 @@ describe("Testing examples", () => {
expect(streamCaptureResult).toEqual(["one", "two", "three"]);
});
it("closureStreamScopes.aqua simple", async () => {
let result = await simpleStreamScopeCall();
// it is not hanging
expect(result).toEqual(["result"]);
});
it("closureStreamScopes.aqua complex", async () => {
let result = await complexStreamScopeCall();
expect(result).toEqual([["something in INSIDE", "something out INSIDE"], ["something out OUTSIDE"]]);
});
// TODO: Unskip this after LNG-226 is fixed
it.skip("streamCapture.aqua return", async () => {
let streamCaptureResult = await streamCaptureReturnCall();

View File

@ -1,10 +1,10 @@
import {
test,
registerTestService,
registerTestServiceClosureArrowCapture,
} from "../compiled/examples/closureArrowCapture.js";
export async function closureArrowCaptureCall(s: string) {
registerTestService("test-service", {
registerTestServiceClosureArrowCapture("test-service", {
call: (s: string) => {
return "call: " + s;
},

View File

@ -0,0 +1,12 @@
import {
simpleTest,
complexTest
} from "../compiled/examples/closureStreamScopes.js";
export async function complexStreamScopeCall(): Promise<[string[], string[]]> {
return complexTest();
}
export async function simpleStreamScopeCall(): Promise<string[]> {
return simpleTest();
}

View File

@ -1,6 +1,6 @@
import {
multiRecStream,
registerTestService,
registerTestServiceMultiRec,
} from "../../compiled/examples/recursiveStreams/multiRec.js";
export async function multiRecStreamCall(
@ -8,7 +8,7 @@ export async function multiRecStreamCall(
target: number,
handle: (i: number) => number[],
): Promise<number[]> {
registerTestService({ handle });
registerTestServiceMultiRec({ handle });
return await multiRecStream(init, target);
}

View File

@ -1,7 +1,7 @@
import {
test,
testCapture,
registerTestService,
registerTestServiceAsAbility,
} from "../compiled/examples/servicesAsAbilities.js";
const serviceIds = {
@ -37,7 +37,7 @@ export const expectedServiceResults = serviceIdsSequence.flatMap((id) =>
export async function servicesAsAbilitiesCall() {
Object.entries(serviceIds).forEach(([_key, id], _idx) =>
registerTestService(id, {
registerTestServiceAsAbility(id, {
concatId: (s: string) => {
return `${id}: ${s}`;
},
@ -57,7 +57,7 @@ export const expectedServiceCaptureResults = [
export async function servicesAsAbilitiesCaptureCall() {
["resolved-id-in-capture", "default-id"].forEach((id) =>
registerTestService(id, {
registerTestServiceAsAbility(id, {
concatId: (s: string) => {
return `${id}: ${s}`;
},

View File

@ -77,7 +77,7 @@ object TypeJs {
def typeList(types: Iterable[(String, Type)]): Dictionary[TypeJs] =
js.Dictionary(types.map { case (n, t) =>
(n, TypeJs.fromType(t))
}.toSeq: _*)
}.toSeq*)
def fromType(t: Type): TypeJs = {
t match

View File

@ -9,9 +9,11 @@ import aqua.raw.value.{ValueRaw, VarRaw}
import aqua.types.*
import cats.data.{Chain, IndexedStateT, State, StateT}
import cats.free.Cofree
import cats.kernel.Semigroup
import cats.syntax.applicative.*
import cats.syntax.bifunctor.*
import cats.syntax.flatMap.*
import cats.syntax.foldable.*
import cats.syntax.option.*
import cats.syntax.semigroup.*
@ -146,6 +148,7 @@ object ArrowInliner extends Logging {
outsideDeclaredStreams: Set[String]
): State[S, InlineResult] = for {
callableFuncBodyNoTopology <- TagInliner.handleTree(fn.body)
callableFuncBody =
fn.capturedTopology
.fold(SeqModel)(ApplyTopologyModel.apply)
@ -173,14 +176,15 @@ object ArrowInliner extends Logging {
// find and get resolved arrows if we return them from the function
returnedArrows = rets.collect { case VarModel(name, _: ArrowType, _) => name }.toSet
arrowsToSave <- Arrows[S].pickArrows(returnedArrows)
arrowsFromClosures <- Arrows[S].pickArrows(returnedArrows)
arrowsToSave = arrowsFromAbilities ++ arrowsFromClosures
body = SeqModel.wrap(callableFuncBody :: ops)
} yield InlineResult(
body,
rets,
varsFromAbilities,
arrowsFromAbilities ++ arrowsToSave
arrowsToSave
)
/**
@ -236,7 +240,7 @@ object ArrowInliner extends Logging {
case arrow @ (_, ValueModel.Arrow(_, _)) =>
arrow.some
case (_, m) =>
internalError(s"($m) cannot be an arrow")
internalError(s"($m) cannot be an arrow for '$abilityName' ability")
}
}
@ -268,7 +272,7 @@ object ArrowInliner extends Logging {
case (_, ValueModel.Arrow(vm, _)) =>
arrows.get(vm.name).map(vm.name -> _)
case (_, m) =>
internalError(s"($m) cannot be an arrow")
internalError(s"($m) cannot be an arrow for '$name' ability")
}
}
@ -324,14 +328,10 @@ object ArrowInliner extends Logging {
* Correctly rename captured values and arrows of a function
*
* @param fn Function
* @param exports Exports state before calling/inlining
* @param arrows Arrows state before calling/inlining
* @return Renamed values and arrows
*/
def renameCaptured[S: Mangler](
fn: FuncArrow,
exports: Map[String, ValueModel],
arrows: Map[String, FuncArrow]
fn: FuncArrow
): State[S, (Renamed[ValueModel], Renamed[FuncArrow])] = {
// Gather abilities related values
val abilitiesValues = fn.capturedValues.collect {
@ -418,7 +418,7 @@ object ArrowInliner extends Logging {
otherValues
)
otherArrowsValuesRenamed = Renamed(
otherValuesRenamed.renames.filterKeys(otherArrowsValues.keySet).toMap,
otherValuesRenamed.renames.view.filterKeys(otherArrowsValues.keySet).toMap,
otherArrowsValues.renamed(otherValuesRenamed.renames)
)
@ -481,9 +481,6 @@ object ArrowInliner extends Logging {
): State[S, (FuncArrow, OpModel.Tree)] = for {
args <- ArgsCall(fn.arrowType.domain, call.args).pure[State[S, *]]
argNames = args.argNames
capturedNames = fn.capturedValues.keySet ++ fn.capturedArrows.keySet
/**
* Substitute all arguments inside function body.
* Data arguments could be passed as variables or values (expressions),
@ -497,7 +494,7 @@ object ArrowInliner extends Logging {
arrowRenames = args.arrowArgsRenames
abRenames = args.abilityArgsRenames
captured <- renameCaptured(fn, exports, arrows)
captured <- renameCaptured(fn)
(capturedValues, capturedArrows) = captured
/**
@ -523,11 +520,6 @@ object ArrowInliner extends Logging {
renamedCanonStreams ++
streamRenames
/**
* TODO: Optimize resolve.
* It seems that resolving whole `exports`
* and `arrows` is not necessary.
*/
arrowsResolved = arrows ++ capturedArrows.renamed
exportsResolved = exports ++ data.renamed ++ capturedValues.renamed

View File

@ -79,33 +79,48 @@ object TagInliner extends Logging {
prefix: Option[OpModel.Tree] = None
) extends TagInlined[S](prefix)
/**
* Tag inlining emitted computation
* that envelopes children computation
*
* @param model computation producing model based on children computation
*/
case Around[S](
model: Chain[State[S, OpModel.Tree]] => State[S, OpModel.Tree],
prefix: Option[OpModel.Tree] = None
) extends TagInlined[S](prefix)
/**
* Finalize inlining, construct a tree
*
* @param children Children results
* @return Result of inlining
*/
def build(children: Chain[OpModel.Tree]): State[T, OpModel.Tree] = {
def toSeqModel(tree: OpModel.Tree | Chain[OpModel.Tree]): State[T, OpModel.Tree] = {
val treeChain = tree match {
case c: Chain[OpModel.Tree] => c
def build(children: Chain[State[T, OpModel.Tree]]): State[T, OpModel.Tree] = {
def prefixSeq(sub: OpModel.Tree | Chain[OpModel.Tree]) = {
val tree = sub match {
case t: OpModel.Tree => Chain.one(t)
case c: Chain[OpModel.Tree] => c
}
State.pure(SeqModel.wrap(Chain.fromOption(prefix) ++ treeChain))
SeqModel.wrap(Chain.fromOption(prefix) ++ tree)
}
this match {
case Empty(_) =>
toSeqModel(children)
children.sequence.map(prefixSeq)
case Single(model, _) =>
toSeqModel(model.wrap(children))
children.sequence.map(model.wrap).map(prefixSeq)
case Mapping(toModel, _) =>
toSeqModel(toModel(children))
children.sequence.map(toModel).map(prefixSeq)
case After(model, _) =>
model.flatMap(m => toSeqModel(m.wrap(children)))
for {
c <- children.sequence
m <- model
} yield prefixSeq(m.wrap(c))
case Around(model, _) =>
model(children).map(prefixSeq)
}
}
}
@ -166,26 +181,6 @@ object TagInliner extends Logging {
}
}
private def flatCanonStream[S: Mangler](
canonV: VarModel,
op: Option[OpModel.Tree]
): State[S, (ValueModel, Option[OpModel.Tree])] = {
if (canonV.properties.nonEmpty) {
val apName = canonV.name + "_flatten"
Mangler[S].findAndForbidName(apName).map { apN =>
val apV = VarModel(apN, canonV.`type`)
val apOp = FlattenModel(canonV, apN).leaf
(
apV,
combineOpsWithSeq(op, apOp.some)
)
}
} else {
State.pure((canonV, op))
}
}
/**
* Processes a single [[RawTag]] that may lead to many changes, including calling [[ArrowInliner]]
*
@ -206,40 +201,13 @@ object TagInliner extends Logging {
)
case IfTag(valueRaw) =>
IfTagInliner(valueRaw).inlined.map(inlined =>
TagInlined.Mapping(
toModel = inlined.toModel,
prefix = inlined.prefix
)
)
IfTagInliner(valueRaw).inlined
case TryTag => pure(XorModel)
case TryTag =>
TryTagInliner.inlined
case ForTag(item, iterable, mode) =>
for {
vp <- valueToModel(iterable)
flattened <- mode match {
case ForTag.Mode.RecMode => State.pure(vp)
case _ => flat(vp._1, vp._2)
}
(v, p) = flattened
n <- Mangler[S].findAndForbidName(item)
elementType = iterable.`type` match {
case b: CollectionType => b.element
case _ =>
internalError(
s"non-box type variable '$iterable' in 'for' expression."
)
}
_ <- Exports[S].resolved(item, VarModel(n, elementType))
modeModel = mode match {
case ForTag.Mode.SeqMode | ForTag.Mode.TryMode => ForModel.Mode.Null
case ForTag.Mode.ParMode | ForTag.Mode.RecMode => ForModel.Mode.Never
}
} yield TagInlined.Single(
model = ForModel(n, v, modeModel),
prefix = p
)
ForTagInliner(item, iterable, mode).inlined
case PushToStreamTag(operand, exportTo) =>
(
@ -369,24 +337,13 @@ object TagInliner extends Logging {
}
} yield model.fold(TagInlined.Empty())(m => TagInlined.Single(model = m))
case RestrictionTag(name, typ) =>
// Rename restriction after children are inlined with new exports
TagInlined
.After(
for {
exps <- Exports[S].exports
model = exps.get(name).collect { case VarModel(n, _, _) =>
RestrictionModel(n, typ)
}
} yield model.getOrElse(RestrictionModel(name, typ))
)
.pure
case DeclareStreamTag(value) =>
value match
case VarRaw(name, t) =>
Exports[S].resolved(name, VarModel(name, t)).as(TagInlined.Empty())
case _ => none
case VarRaw(name, t: StreamType) =>
for {
_ <- Exports[S].resolved(name, VarModel(name, t))
} yield TagInlined.Empty()
case _ => internalError(s"Cannot declare $value as stream, because it is not a stream type")
case ServiceIdTag(id, serviceType, name) =>
for {
@ -448,7 +405,7 @@ object TagInliner extends Logging {
for {
headInlined <- f(cf.head)
tail <- StateT.liftF(cf.tail)
children <- tail.traverse(traverseS[S](_, f))
children = tail.map(traverseS[S](_, f))
inlined <- headInlined.build(children)
} yield inlined

View File

@ -7,6 +7,7 @@ import aqua.types.*
import cats.data.State
import cats.instances.list.*
import cats.syntax.apply.*
import cats.syntax.functor.*
import cats.syntax.option.*
import cats.syntax.show.*
@ -20,6 +21,7 @@ import cats.syntax.traverse.*
*/
trait Arrows[S] extends Scoped[S] {
self =>
def save(name: String, arrow: FuncArrow): State[S, Unit]
/**
@ -44,8 +46,7 @@ trait Arrows[S] extends Scoped[S] {
/**
* Save arrows to the state [[S]]
*
* @param arrows
* Resolved arrows, accessible by key name which could differ from arrow's name
* @param arrows Resolved arrows, accessible by key name which could differ from arrow's name
*/
final def resolved(arrows: Map[String, FuncArrow]): State[S, Unit] =
arrows.toList.traverse(save).void
@ -53,22 +54,18 @@ trait Arrows[S] extends Scoped[S] {
/**
* All arrows available for use in scope
*/
val arrows: State[S, Map[String, FuncArrow]]
def arrows: State[S, Map[String, FuncArrow]]
/**
* Pick a subset of arrows by names
*
* @param names
* What arrows should be taken
* @param names What arrows should be taken
*/
def pickArrows(names: Set[String]): State[S, Map[String, FuncArrow]] =
arrows.map(_.view.filterKeys(names).toMap)
/**
* Take arrows selected by the function call arguments
*
* @param args
* @return
*/
def argsArrows(args: ArgsCall): State[S, Map[String, FuncArrow]] =
arrows.map(args.arrowArgsMap)
@ -76,26 +73,21 @@ trait Arrows[S] extends Scoped[S] {
/**
* Changes the [[S]] type to [[R]]
*
* @param f
* Lens getter
* @param g
* Lens setter
* @tparam R
* New state type
* @param f Lens getter
* @param g Lens setter
*/
def transformS[R](f: R => S, g: (R, S) => R): Arrows[R] = new Arrows[R] {
override def save(name: String, arrow: FuncArrow): State[R, Unit] =
self.save(name, arrow).transformS(f, g)
override val arrows: State[R, Map[String, FuncArrow]] = self.arrows.transformS(f, g)
override def arrows: State[R, Map[String, FuncArrow]] = self.arrows.transformS(f, g)
override val purge: State[R, R] =
override protected def purge: State[R, R] =
self.purgeR(f, g)
override protected def fill(s: R): State[R, Unit] =
self.fillR(s, f, g)
override protected def set(r: R): State[R, Unit] =
self.setR(f, g)(r)
}
}
@ -119,7 +111,7 @@ object Arrows {
}
}
def apply[S](implicit arrows: Arrows[S]): Arrows[S] = arrows
def apply[S](using arrows: Arrows[S]): Arrows[S] = arrows
// Default implementation with the most straightforward state just a Map
object Simple extends Arrows[Map[String, FuncArrow]] {
@ -127,16 +119,13 @@ object Arrows {
override def save(name: String, arrow: FuncArrow): State[Map[String, FuncArrow], Unit] =
State.modify(_ + (name -> arrow))
override val arrows: State[Map[String, FuncArrow], Map[String, FuncArrow]] =
override def arrows: State[Map[String, FuncArrow], Map[String, FuncArrow]] =
State.get
override val purge: State[Map[String, FuncArrow], Map[String, FuncArrow]] =
for {
s <- State.get
_ <- State.set(Map.empty)
} yield s
State.get <* State.set(Map.empty)
override protected def fill(s: Map[String, FuncArrow]): State[Map[String, FuncArrow], Unit] =
override def set(s: Map[String, FuncArrow]): State[Map[String, FuncArrow], Unit] =
State.set(s)
}
}

View File

@ -1,10 +1,13 @@
package aqua.model.inline.state
import aqua.model.ValueModel.Ability
import aqua.model.ValueModel.{Ability, Stream}
import aqua.model.{LiteralModel, ValueModel, VarModel}
import aqua.types.StreamType
import aqua.types.{AbilityType, GeneralAbilityType, NamedType}
import cats.data.{NonEmptyList, State}
import cats.syntax.apply.*
import cats.syntax.traverse.*
/**
* Exports trace values available in the scope
@ -49,11 +52,6 @@ trait Exports[S] extends Scoped[S] {
*/
def getLastVarName(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
* @param exports
@ -76,7 +74,20 @@ trait Exports[S] extends Scoped[S] {
/**
* Get all the values available in the scope
*/
val exports: State[S, Map[String, ValueModel]]
def exports: State[S, Map[String, ValueModel]]
def deleteStreams(names: Set[String]): State[S, Unit]
def streams: State[S, Map[String, StreamType]]
def streamScope[T](inside: State[S, T]): State[S, (T, Map[String, StreamType])] =
for {
streamsBefore <- streams
tree <- inside
streamsAfter <- streams
streams = streamsAfter.removedAll(streamsBefore.keySet)
_ <- deleteStreams(streams.keySet)
} yield (tree, streams)
final def gather(names: Seq[String]): State[S, Map[String, ValueModel]] =
exports.map(Exports.gatherFrom(names, _))
@ -92,6 +103,12 @@ trait Exports[S] extends Scoped[S] {
override def resolved(exports: Map[String, ValueModel]): State[R, Unit] =
self.resolved(exports).transformS(f, g)
override def streams: State[R, Map[String, StreamType]] =
self.streams.transformS(f, g)
override def deleteStreams(names: Set[String]): State[R, Unit] =
self.deleteStreams(names).transformS(f, g)
override def resolveAbilityField(
abilityExportName: String,
fieldName: String,
@ -105,23 +122,20 @@ trait Exports[S] extends Scoped[S] {
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)
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 def exports: State[R, Map[String, ValueModel]] =
self.exports.transformS(f, g)
override val purge: State[R, R] =
override protected def purge: State[R, R] =
self.purgeR(f, g)
override protected def fill(s: R): State[R, Unit] =
self.fillR(s, f, g)
override protected def set(r: R): State[R, Unit] =
self.setR(f, g)(r)
}
}
@ -166,7 +180,16 @@ object Exports {
}
}
object Simple extends Exports[Map[String, ValueModel]] {
/**
* @param values list of all values in scope.
* @param streams list of opened streams. Merged between states.
*/
case class ExportsState(
values: Map[String, ValueModel] = Map.empty,
streams: Map[String, StreamType] = Map.empty
)
object Simple extends Exports[ExportsState] {
// Make links from one set of abilities to another (for ability assignment)
private def getAbilityPairs(
@ -192,69 +215,80 @@ object Exports {
override def resolved(
exportName: String,
value: ValueModel
): State[Map[String, ValueModel], Unit] = State.modify { state =>
): State[ExportsState, Unit] = State.modify { state =>
value match {
case Ability(vm, at) if vm.properties.isEmpty =>
val pairs = getAbilityPairs(vm.name, exportName, at, state)
state ++ pairs.toList.toMap + (exportName -> value)
case _ => state + (exportName -> value)
val pairs = getAbilityPairs(vm.name, exportName, at, state.values)
state.copy(values = state.values ++ pairs.toList.toMap + (exportName -> value))
case Stream(VarModel(streamName, _, _), st) if exportName == streamName =>
state.copy(
values = state.values + (exportName -> value),
streams = state.streams + (exportName -> st)
)
case _ => state.copy(values = state.values + (exportName -> value))
}
}
override def getLastVarName(name: String): State[Map[String, ValueModel], Option[String]] =
State.get.map(st => getLastValue(name, st).collect { case VarModel(name, _, _) => name })
override def getLastVarName(name: String): State[ExportsState, Option[String]] =
State.get.map(st =>
getLastValue(name, st.values).collect { case VarModel(name, _, _) => name }
)
override def resolved(exports: Map[String, ValueModel]): State[Map[String, ValueModel], Unit] =
State.modify(_ ++ exports)
override def resolved(exports: Map[String, ValueModel]): State[ExportsState, Unit] =
State.modify(st => st.copy(values = st.values ++ exports))
override def streams: State[ExportsState, Map[String, StreamType]] =
State.get.map(_.streams)
override def deleteStreams(names: Set[String]): State[ExportsState, Unit] =
State.modify(st => st.copy(streams = st.streams -- names))
override def resolveAbilityField(
abilityExportName: String,
fieldName: String,
value: ValueModel
): State[Map[String, ValueModel], Unit] =
State.modify(_ + (AbilityType.fullName(abilityExportName, fieldName) -> value))
): State[ExportsState, Unit] =
State.modify(st =>
st.copy(values = st.values + (AbilityType.fullName(abilityExportName, fieldName) -> value))
)
override def copyWithAbilityPrefix(
prefix: String,
newPrefix: String
): State[Map[String, ValueModel], Unit] =
): State[ExportsState, Unit] =
State.modify { state =>
state.flatMap {
val newValues = state.values.flatMap {
case (k, v) if k.startsWith(prefix) =>
List(k.replaceFirst(prefix, newPrefix) -> v, k -> v)
case (k, v) => List(k -> v)
}
state.copy(values = newValues)
}
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 getKeys: State[ExportsState, Set[String]] = State.get.map(_.values.keySet)
override def getAbilityField(
name: String,
field: String
): State[Map[String, ValueModel], Option[ValueModel]] =
State.get.map(_.get(AbilityType.fullName(name, field)))
): State[ExportsState, Option[ValueModel]] =
State.get.map(_.values.get(AbilityType.fullName(name, field)))
override val exports: State[Map[String, ValueModel], Map[String, ValueModel]] =
State.get
override val exports: State[ExportsState, Map[String, ValueModel]] =
State.get.map(_.values)
override val purge: State[Map[String, ValueModel], Map[String, ValueModel]] =
override val purge: State[ExportsState, ExportsState] =
for {
s <- State.get
_ <- State.set(Map.empty)
} yield s
st <- State.get
// HACK: refactor
_ <- State.modify[ExportsState](st => ExportsState(streams = st.streams))
} yield st
override protected def fill(s: Map[String, ValueModel]): State[Map[String, ValueModel], Unit] =
State.set(s)
override def set(s: ExportsState): State[ExportsState, Unit] = {
for {
st <- State.get
// HACK: refactor
_ <- State.set(s.copy(streams = st.streams ++ s.streams))
} yield {}
}
}
}

View File

@ -1,6 +1,7 @@
package aqua.model.inline.state
import aqua.mangler.ManglerState
import aqua.model.inline.state.Exports.ExportsState
import aqua.model.inline.state.{Arrows, Counter, Exports, Mangler}
import aqua.model.{FuncArrow, ValueModel}
import aqua.raw.arrow.FuncRaw
@ -26,7 +27,7 @@ import scribe.Logging
*/
case class InliningState(
noNames: ManglerState = ManglerState(),
resolvedExports: Map[String, ValueModel] = Map.empty,
resolvedExports: ExportsState = ExportsState(),
resolvedArrows: Map[String, FuncArrow] = Map.empty,
instructionCounter: Int = 0,
config: Config.Values = Config.Values.default

View File

@ -1,39 +1,39 @@
package aqua.model.inline.state
import cats.data.State
import cats.syntax.apply.*
import cats.syntax.functor.*
/**
* Common transformations to make an isolated scope for the state [[S]]
* @tparam S State
*/
trait Scoped[S] {
// Remove everything from the state
val purge: State[S, S]
// Put [[s]] to the state
protected def fill(s: S): State[S, Unit]
/**
* Clear the state, run [[scoped]], then recover the initial state
* @param scoped What to run with empty [[S]]
* @tparam T Return type
* @return Value returned by [[scoped]]
*/
def scope[T](scoped: State[S, T]): State[S, T] =
for {
r <- purge
t <- scoped
_ <- fill(r)
_ <- set(r)
} yield t
// For transformS
protected def purgeR[R](f: R => S, g: (R, S) => R): State[R, R] =
for {
r <- State.get[R]
s <- purge.transformS(f, g)
} yield g(r, s)
protected def purge: State[S, S]
protected def set(s: S): State[S, Unit]
protected final def purgeR[R](f: R => S, g: (R, S) => R): State[R, R] =
(State.get[R], purge.transformS(f, g)).mapN(g)
protected final def setR[R](f: R => S, g: (R, S) => R)(r: R): State[R, Unit] =
set(f(r)).transformS(f, g)
private final def get: State[S, S] = for {
s <- purge
_ <- set(s)
} yield s
// For transformS
protected def fillR[R](s: R, f: R => S, g: (R, S) => R): State[R, Unit] =
fill(f(s)).transformS(f, g)
}

View File

@ -0,0 +1,54 @@
package aqua.model.inline.tag
import aqua.errors.Errors.internalError
import aqua.helpers.syntax.reader.*
import aqua.model.*
import aqua.model.ValueModel
import aqua.model.inline.Inline.parDesugarPrefixOpt
import aqua.model.inline.RawValueInliner.valueToModel
import aqua.model.inline.TagInliner.TagInlined
import aqua.model.inline.TagInliner.flat
import aqua.model.inline.state.*
import aqua.raw.ops.ForTag
import aqua.raw.value.ValueRaw
import aqua.types.CollectionType
import aqua.types.StreamType
import cats.Eval
import cats.data.Reader
import cats.data.{Chain, State}
import cats.syntax.apply.*
import cats.syntax.flatMap.*
final case class ForTagInliner(
item: String,
iterable: ValueRaw,
mode: ForTag.Mode
) {
def inlined[S: Mangler: Exports: Arrows: Config]: State[S, TagInlined[S]] = for {
vp <- valueToModel(iterable)
flattened <- mode match {
case ForTag.Mode.RecMode => State.pure(vp)
case _ => flat.tupled(vp)
}
(v, p) = flattened
n <- Mangler[S].findAndForbidName(item)
elementType = iterable.`type` match {
case b: CollectionType => b.element
case _ =>
internalError(
s"non-box type variable '$iterable' in 'for' expression."
)
}
_ <- Exports[S].resolved(item, VarModel(n, elementType))
modeModel = mode match {
case ForTag.Mode.SeqMode | ForTag.Mode.TryMode => ForModel.Mode.Null
case ForTag.Mode.ParMode | ForTag.Mode.RecMode => ForModel.Mode.Never
}
model = ForModel(n, v, modeModel)
} yield TagInlined.Around(
model = StreamRestrictions.restrictStreams(model.wrap),
prefix = p
)
}

View File

@ -5,13 +5,13 @@ import aqua.model.*
import aqua.model.ValueModel
import aqua.model.inline.Inline.parDesugarPrefixOpt
import aqua.model.inline.RawValueInliner.valueToModel
import aqua.model.inline.TagInliner.canonicalizeIfStream
import aqua.model.inline.TagInliner.{TagInlined, canonicalizeIfStream}
import aqua.model.inline.state.*
import aqua.raw.value.ApplyBinaryOpRaw.Op as BinOp
import aqua.raw.value.{ApplyBinaryOpRaw, ValueRaw}
import aqua.types.StreamType
import cats.Eval
import cats.data.Reader
import cats.data.{Chain, State}
import cats.syntax.apply.*
import cats.syntax.flatMap.*
@ -21,8 +21,8 @@ final case class IfTagInliner(
) {
import IfTagInliner.*
def inlined[S: Mangler: Exports: Arrows: Config]: State[S, IfTagInlined] = for {
cond <- (valueRaw match {
def inlined[S: Mangler: Exports: Arrows: Config]: State[S, TagInlined[S]] = for {
cond <- valueRaw match {
// Optimize in case last operation is equality check
case ApplyBinaryOpRaw(op @ (BinOp.Eq | BinOp.Neq), left, right, _) =>
(
@ -44,13 +44,15 @@ final case class IfTagInliner(
(prefix, valueModel, compareModel, shouldMatch)
}
})
}
(prefix, leftValue, rightValue, shouldMatch) = cond
noProp <- Config[S].noErrorPropagation.toState
model = if (noProp) toModelNoProp else toModel
} yield IfTagInlined(
prefix,
model(leftValue, rightValue, shouldMatch)
modelByChildren = model(leftValue, rightValue, shouldMatch)
stateModel = StreamRestrictions.restrictStreams[S](modelByChildren)
} yield TagInlined.Around(
prefix = prefix,
model = stateModel
)
private def toModelNoProp(
@ -185,11 +187,6 @@ final case class IfTagInliner(
object IfTagInliner {
final case class IfTagInlined(
prefix: Option[OpModel.Tree],
toModel: Chain[OpModel.Tree] => OpModel.Tree
)
private def restrictErrors(
name: String*
)(tree: OpModel.Tree): OpModel.Tree =

View File

@ -0,0 +1,29 @@
package aqua.model.inline.tag
import aqua.model.inline.state.{Arrows, Config, Exports, Mangler}
import aqua.model.{OpModel, RestrictionModel}
import aqua.types.StreamType
import cats.data.{Chain, State}
import cats.syntax.traverse.*
object StreamRestrictions {
// restrict streams that are generated in a tree
def restrictStreams[S: Mangler: Exports: Arrows: Config](
childrenToModel: Chain[OpModel.Tree] => OpModel.Tree
)(children: Chain[State[S, OpModel.Tree]]): State[S, OpModel.Tree] =
children.traverse(restrictStreamsAround).map(childrenToModel)
/**
* Restrict streams that are generated in a tree
*/
private def restrictStreamsAround[S: Mangler: Exports: Arrows: Config](
getTree: State[S, OpModel.Tree]
): State[S, OpModel.Tree] =
Exports[S].streamScope(getTree).map { case (tree, streams) =>
streams.toList.foldLeft(tree) { case (acc, (name, st)) =>
RestrictionModel(name, st).wrap(acc)
}
}
}

View File

@ -0,0 +1,11 @@
package aqua.model.inline.tag
import aqua.model.inline.state.{Arrows, Config, Exports, Mangler}
import aqua.model.inline.TagInliner.TagInlined
import aqua.model.XorModel
import cats.data.State
object TryTagInliner {
def inlined[S: Mangler: Exports: Arrows: Config]: State[S, TagInlined[S]] =
State.pure(TagInlined.Around(model = StreamRestrictions.restrictStreams(XorModel.wrap)))
}

View File

@ -134,11 +134,9 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers with Inside {
.callArrow[InliningState](
FuncArrow(
"stream-callback",
RestrictionTag(streamVar.name, streamType).wrap(
SeqTag.wrap(
DeclareStreamTag(streamVar).leaf,
CallArrowRawTag.func("cb", Call(streamVar :: Nil, Nil)).leaf
)
),
ArrowType(
ProductType.labelled(
@ -166,7 +164,6 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers with Inside {
.value
model.equalsOrShowDiff(
RestrictionModel(streamVar.name, streamType).wrap(
MetaModel
.CallArrowModel("cb")
.wrap(
@ -182,7 +179,6 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers with Inside {
).leaf
)
)
)
) should be(true)
}
@ -283,7 +279,8 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers with Inside {
* stream <<- "asd"
* <- stream
*/
it should "rename restricted stream correctly" in {
// IGNORED: streams are not restricted in function bodies for now
it should "rename restricted stream correctly" ignore {
val streamType = StreamType(ScalarType.string)
val someStr = VarRaw("someStr", streamType)
@ -319,7 +316,6 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers with Inside {
val newFunc = FuncArrow(
"newFunc",
RestrictionTag(streamVar.name, streamType).wrap(
SeqTag.wrap(
CallArrowRawTag
.func(
@ -342,7 +338,6 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers with Inside {
ReturnTag(
NonEmptyList.one(flatStreamVar)
).leaf
)
),
ArrowType(
ProductType(Nil),
@ -440,7 +435,6 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers with Inside {
val testReturnStream = FuncArrow(
"testReturnStream",
RestrictionTag(streamVar.name, streamType).wrap(
SeqTag.wrap(
CallArrowRawTag
.func(
@ -463,7 +457,6 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers with Inside {
ReturnTag(
NonEmptyList.one(flatStreamVar)
).leaf
)
),
ArrowType(
ProductType(Nil),
@ -883,11 +876,9 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers with Inside {
.callArrow[InliningState](
FuncArrow(
"stream-callback",
RestrictionTag(streamVar.name, streamType).wrap(
SeqTag.wrap(
DeclareStreamTag(streamVar).leaf,
CallArrowRawTag.func("cb", Call(streamVarLambda :: Nil, Nil)).leaf
)
),
ArrowType(
ProductType.labelled(
@ -1399,7 +1390,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers with Inside {
.one(
outterRes
)
).leaf: _*
).leaf*
)
val outer = FuncArrow(

View File

@ -3,6 +3,7 @@ package aqua.model.inline
import aqua.mangler.ManglerState
import aqua.model.*
import aqua.model.inline.raw.StreamGateInliner
import aqua.model.inline.state.Exports.ExportsState
import aqua.model.inline.state.InliningState
import aqua.raw.value.*
import aqua.types.*
@ -204,7 +205,7 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers with Inside {
valueToModel[InliningState](`raw res.c`)
.runA(
InliningState(resolvedExports =
Map("res" -> VarModel("a", aType, Chain.one(IntoFieldModel("b", bType))))
ExportsState(Map("res" -> VarModel("a", aType, Chain.one(IntoFieldModel("b", bType)))))
)
)
.value shouldBe (
@ -608,7 +609,7 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers with Inside {
expr <- genAllExprs(perm)
} {
val state = InliningState(
resolvedExports = vars.map(v => v.name -> VarModel.fromVarRaw(v)).toMap
resolvedExports = ExportsState(vars.map(v => v.name -> VarModel.fromVarRaw(v)).toMap)
)
val (model, inline) = valueToModel[InliningState](expr).runA(state).value

View File

@ -1,16 +1,14 @@
package aqua.model.inline
import aqua.model.ForModel
import aqua.model.ValueModel
import aqua.model.*
import aqua.model.inline.TagInliner.TagInlined
import aqua.model.inline.state.Exports.ExportsState
import aqua.model.inline.state.InliningState
import aqua.model.{LiteralModel, OpModel, SeqModel}
import aqua.raw.ops.ForTag
import aqua.raw.ops.{Call, CanonicalizeTag, FlattenTag}
import aqua.raw.value.ValueRaw
import aqua.raw.value.VarRaw
import aqua.raw.ops.{Call, CanonicalizeTag, FlattenTag, ForTag}
import aqua.raw.value.{ValueRaw, VarRaw}
import aqua.types.{ScalarType, StreamType}
import cats.data.{Chain, State}
import cats.syntax.show.*
import org.scalatest.Inside
import org.scalatest.flatspec.AnyFlatSpec
@ -28,7 +26,7 @@ class TagInlinerSpec extends AnyFlatSpec with Matchers with Inside {
.run(InliningState())
.value
state.resolvedExports(canonTo) shouldBe LiteralModel(
state.resolvedExports.values(canonTo) shouldBe LiteralModel(
ValueRaw.Nil.value,
ValueRaw.Nil.baseType
)
@ -48,7 +46,7 @@ class TagInlinerSpec extends AnyFlatSpec with Matchers with Inside {
.run(InliningState())
.value
state.resolvedExports(canonTo) shouldBe LiteralModel(
state.resolvedExports.values(canonTo) shouldBe LiteralModel(
ValueRaw.Nil.value,
ValueRaw.Nil.baseType
)
@ -68,15 +66,20 @@ class TagInlinerSpec extends AnyFlatSpec with Matchers with Inside {
.tagToModel[InliningState](tag)
.run(
InliningState(
resolvedExports = Map(
resolvedExports = ExportsState(
Map(
iterableRaw.name -> iterableModel
)
)
)
)
.value
inside(inlined) { case TagInlined.Single(ForModel(_, iter, ForModel.Mode.Never), _) =>
inside(inlined) { case TagInlined.Around(st, _) =>
inside(st(Chain.empty).runA(state).value.head) {
case ForModel(_, iter, ForModel.Mode.Never) =>
iter shouldBe iterableModel
}
}
}
}

View File

@ -11,8 +11,9 @@ case class FuncRaw(
override def rawPartType: Type = arrow.`type`
// vars that we capture from external space (outer functions, etc)
lazy val capturedVars: Set[String] = {
val freeBodyVars = arrow.body.usesVarNames.value
val freeBodyVars: Set[String] = arrow.body.usesVarNames.value
val argsNames = arrow.`type`.domain
.toLabelledList()
.map { case (name, _) => name }

View File

@ -4,6 +4,7 @@ import aqua.raw.arrow.FuncRaw
import aqua.raw.value.{CallArrowRaw, CallServiceRaw, ValueRaw, VarRaw}
import aqua.tree.{TreeNode, TreeNodeCompanion}
import aqua.types.*
import cats.Show
import cats.data.{Chain, NonEmptyList}
import cats.free.Cofree
@ -158,14 +159,6 @@ case class NextTag(item: String) extends RawTag {
override def mapValues(f: ValueRaw => ValueRaw): RawTag = this
}
case class RestrictionTag(name: String, `type`: Type) extends SeqGroupTag {
override def restrictsVarNames: Set[String] = Set(name)
override def renameExports(map: Map[String, String]): RawTag =
copy(name = map.getOrElse(name, name))
}
case class ForTag(item: String, iterable: ValueRaw, mode: ForTag.Mode) extends SeqGroupTag {
override def restrictsVarNames: Set[String] = Set(item)

View File

@ -1,11 +1,10 @@
package aqua.raw.ops
import aqua.raw.value.{LiteralRaw, ValueRaw}
import aqua.raw.value.{LiteralRaw, ValueRaw, VarRaw}
import cats.data.Chain
import cats.free.Cofree
import cats.syntax.all.*
import cats.syntax.apply.*
import cats.syntax.flatMap.*
import cats.syntax.foldable.*
import cats.syntax.semigroup.*
import cats.{Eval, Semigroup}
@ -46,11 +45,7 @@ trait RawTagGivens {
Eval.later(acc.foldLeft(tag.definesVarNames)(_ ++ _))
}
/**
* Get all variable names used by this tree
* but not exported in it (free variables).
*/
def usesVarNames: Eval[Set[String]] =
def exportedAndUsesNames: Eval[(Set[String], Set[String])] =
Cofree
.cata(tree)((tag, childs: Chain[(Set[String], Set[String])]) =>
Eval.later {
@ -60,8 +55,19 @@ trait RawTagGivens {
(exports, uses)
}
)
/**
* Get all variable names used by this tree
* but not exported in it (free variables).
*/
def usesVarNames: Eval[Set[String]] =
exportedAndUsesNames
.map { case (_, uses) => uses }
def exportsVarNames: Eval[Set[String]] =
exportedAndUsesNames
.map { case (exports, _) => exports }
private def collect[A](pf: PartialFunction[RawTag, A]): Eval[Chain[A]] =
Cofree.cata(tree)((tag, acc: Chain[Chain[A]]) =>
Eval.later(Chain.fromOption(pf.lift(tag)) ++ acc.flatten)

View File

@ -172,13 +172,6 @@ object ForModel {
ForModel(item, iterable, Mode.Null)
}
// TODO how is it used? remove, if it's not
case class DeclareStreamModel(value: ValueModel) extends NoExecModel {
override def toString: String = s"declare $value"
override def usesVarNames: Set[String] = value.usesVarNames
}
// key must be only string or number
case class InsertKeyValueModel(
key: ValueModel,

View File

@ -1,24 +1,24 @@
package aqua.model.transform.topology
import aqua.model.transform.ModelBuilder
import aqua.model.*
import aqua.res.*
import aqua.raw.ops.Call
import aqua.raw.value.{IntoIndexRaw, LiteralRaw, VarRaw}
import aqua.types.{LiteralType, ScalarType, StreamType}
import aqua.types.ArrayType
import aqua.raw.ConstantRaw.initPeerId
import aqua.model.ForModel
import aqua.model.transform.ModelBuilder
import aqua.raw.ConstantRaw.initPeerId
import aqua.raw.ops.Call
import aqua.raw.value.ValueRaw
import aqua.raw.value.{IntoIndexRaw, LiteralRaw, VarRaw}
import aqua.res.*
import aqua.types.ArrayType
import aqua.types.{LiteralType, ScalarType, StreamType}
import cats.Eval
import cats.data.{Chain, NonEmptyList}
import cats.data.Chain.*
import cats.data.{Chain, NonEmptyList}
import cats.free.Cofree
import cats.syntax.option.*
import cats.syntax.show.*
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import cats.syntax.show.*
import cats.syntax.option.*
class TopologySpec extends AnyFlatSpec with Matchers {
@ -481,7 +481,7 @@ class TopologySpec extends AnyFlatSpec with Matchers {
val streamRawEl = VarRaw("stream", StreamType(ScalarType.string)).withProperty(
IntoIndexRaw(LiteralRaw("2", ScalarType.u32), ScalarType.string)
)
val stream = ValueModel.fromRaw(streamRaw)
val stream = VarModel(streamRaw.name, streamRaw.baseType)
val streamEl = ValueModel.fromRaw(streamRawEl)
val (joinModel, joinRes) = joinModelRes(streamEl)
@ -499,7 +499,6 @@ class TopologySpec extends AnyFlatSpec with Matchers {
)
)
val init = SeqModel.wrap(
DeclareStreamModel(stream).leaf,
OnModel(initPeer, Chain.one(relay)).wrap(
foldModel +:
joinModel :+
@ -534,10 +533,7 @@ class TopologySpec extends AnyFlatSpec with Matchers {
)
)
val expected = SeqRes.wrap(
Chain(
through(relay),
foldRes
) ++
foldRes +:
joinRes :+
callRes(3, initPeer, None, stream :: Nil)
)
@ -552,13 +548,12 @@ class TopologySpec extends AnyFlatSpec with Matchers {
val streamRawEl = VarRaw("stream", StreamType(ScalarType.string)).withProperty(
IntoIndexRaw(LiteralRaw("2", ScalarType.u32), ScalarType.string)
)
val stream = ValueModel.fromRaw(streamRaw)
val stream = VarModel(streamRaw.name, streamRaw.baseType)
val streamEl = ValueModel.fromRaw(streamRawEl)
val (joinModel, joinRes) = joinModelRes(streamEl)
val init = SeqModel.wrap(
DeclareStreamModel(stream).leaf,
OnModel(initPeer, Chain.one(relay)).wrap(
foldPar(
"i",
@ -608,10 +603,7 @@ class TopologySpec extends AnyFlatSpec with Matchers {
)
)
val expected = SeqRes.wrap(
Chain(
through(relay),
fold
) ++
fold +:
joinRes :+
callRes(3, initPeer, None, stream :: Nil)
)

View File

@ -55,26 +55,12 @@ class ArrowSem[S[_]](val expr: ArrowExpr[S]) extends AnyVal {
N: NamesAlgebra[S, Alg],
M: ManglerAlgebra[Alg]
): Alg[Raw] = for {
streamsInScope <- N.streamsDefinedWithinScope()
retValues <- T.endArrowScope(expr.arrowTypeExpr)
// TODO: wrap with local on...via...
retsAndCodomain = retValues zip funcArrow.codomain.toList
(streamThatReturnAsStreamVars, streamThatReturnAsStreamNames) = retsAndCodomain.collect {
case (vr @ VarRaw(name, StreamType(_)), StreamType(_)) => (vr, name)
case (vr @ StreamRaw(_, name, _), StreamType(_)) => (vr, name)
}.unzip
// streams that return as streams and derived to another variable
derivedStreamRetValues <- N
.getDerivedFrom(streamThatReturnAsStreamVars.map(_.varNames))
.map(_.flatten.toSet)
res <- bodyGen match {
case FuncOp(bodyModel) =>
val streamArgNames = funcArrow.domain.labelledStreams.map { case (name, _) => name }
// Remove arguments, and values returned as streams
val localStreams = streamsInScope -- streamArgNames --
streamThatReturnAsStreamNames.toSet -- derivedStreamRetValues
// process stream that returns as not streams and all Apply*Raw
retsAndCodomain.traverse {
@ -124,13 +110,7 @@ class ArrowSem[S[_]](val expr: ArrowExpr[S]) extends AnyVal {
val bodyModified = SeqTag.wrap(
bodyModel +: bodyRets
)
// wrap streams with restrictions
val bodyWithRestrictions =
localStreams.foldLeft(bodyModified) { case (bm, (streamName, streamType)) =>
RestrictionTag(streamName, streamType).wrap(bm)
}
ArrowRaw(funcArrow, retVals, bodyWithRestrictions)
ArrowRaw(funcArrow, retVals, bodyModified)
}
case _ => Raw.error("Invalid arrow body").pure[Alg]

View File

@ -1,13 +1,14 @@
package aqua.semantics.expr.func
import aqua.raw.ops.{AssignmentTag, FuncOp, SeqTag, TryTag}
import aqua.parser.expr.func.CatchExpr
import aqua.raw.value.ValueRaw
import aqua.raw.Raw
import aqua.raw.ops.{AssignmentTag, FuncOp, SeqTag, TryTag}
import aqua.raw.value.ValueRaw
import aqua.semantics.Prog
import aqua.semantics.rules.abilities.AbilitiesAlgebra
import aqua.semantics.rules.locations.LocationsAlgebra
import aqua.semantics.rules.names.NamesAlgebra
import cats.Monad
import cats.syntax.applicative.*
import cats.syntax.flatMap.*
@ -15,7 +16,7 @@ import cats.syntax.functor.*
class CatchSem[S[_]](val expr: CatchExpr[S]) extends AnyVal {
def program[Alg[_]: Monad](implicit
def program[Alg[_]: Monad](using
N: NamesAlgebra[S, Alg],
A: AbilitiesAlgebra[S, Alg],
L: LocationsAlgebra[S, Alg]
@ -26,16 +27,15 @@ class CatchSem[S[_]](val expr: CatchExpr[S]) extends AnyVal {
(_, g: Raw) =>
g match {
case FuncOp(op) =>
for {
restricted <- FuncOpSem.restrictStreamsInScope(op)
tag = TryTag.Catch
TryTag.Catch
.wrap(
SeqTag.wrap(
AssignmentTag(ValueRaw.error, expr.name.value).leaf,
restricted
op
)
)
} yield tag.toFuncOp
.toFuncOp
.pure
case _ =>
Raw.error("Wrong body of the `catch` expression").pure
}

View File

@ -1,16 +1,16 @@
package aqua.semantics.expr.func
import aqua.raw.ops.{FuncOp, IfTag, TryTag}
import aqua.parser.expr.func.ElseOtherwiseExpr
import aqua.raw.Raw
import aqua.raw.ops.{FuncOp, IfTag, TryTag}
import aqua.semantics.Prog
import aqua.semantics.rules.abilities.AbilitiesAlgebra
import aqua.semantics.rules.locations.LocationsAlgebra
import aqua.semantics.rules.names.NamesAlgebra
import cats.Monad
import cats.syntax.applicative.*
import cats.syntax.functor.*
import cats.Monad
class ElseOtherwiseSem[S[_]](val expr: ElseOtherwiseExpr[S]) extends AnyVal {
@ -23,15 +23,14 @@ class ElseOtherwiseSem[S[_]](val expr: ElseOtherwiseExpr[S]) extends AnyVal {
.after((ops: Raw) =>
ops match {
case FuncOp(op) =>
for {
restricted <- FuncOpSem.restrictStreamsInScope(op)
tag = expr.kind
expr.kind
.fold(
ifElse = IfTag.Else,
ifOtherwise = TryTag.Otherwise
)
.wrap(restricted)
} yield tag.toFuncOp
.wrap(op)
.toFuncOp
.pure
case _ =>
val name = expr.kind.fold("`else`", "`otherwise`")
Raw.error(s"Wrong body of the $name expression").pure

View File

@ -7,7 +7,6 @@ import aqua.raw.ops.*
import aqua.raw.ops.ForTag.Mode
import aqua.raw.value.ValueRaw
import aqua.semantics.Prog
import aqua.semantics.expr.func.FuncOpSem
import aqua.semantics.rules.ValuesAlgebra
import aqua.semantics.rules.abilities.AbilitiesAlgebra
import aqua.semantics.rules.names.NamesAlgebra
@ -38,7 +37,6 @@ class ForSem[S[_]](val expr: ForExpr[S]) extends AnyVal {
(iterable, ops: Raw) =>
(iterable, ops) match {
case (Some(vm), FuncOp(op)) =>
FuncOpSem.restrictStreamsInScope(op).map { restricted =>
val mode = expr.mode.fold(ForTag.Mode.SeqMode) {
case ForExpr.Mode.ParMode => ForTag.Mode.ParMode
case ForExpr.Mode.TryMode => ForTag.Mode.TryMode
@ -54,7 +52,7 @@ class ForSem[S[_]](val expr: ForExpr[S]) extends AnyVal {
val forTag = ForTag(expr.item.value, vm, mode).wrap(
innerTag.wrap(
restricted,
op,
NextTag(expr.item.value).leaf
)
)
@ -67,8 +65,7 @@ class ForSem[S[_]](val expr: ForExpr[S]) extends AnyVal {
case _ => forTag
}
result.toFuncOp
}
result.toFuncOp.pure
case _ => Raw.error("Wrong body of the `for` expression").pure[F]
}
)

View File

@ -1,21 +0,0 @@
package aqua.semantics.expr.func
import aqua.raw.ops.{RawTag, RestrictionTag}
import aqua.semantics.rules.names.NamesAlgebra
import cats.{FlatMap, Functor, Monad}
import cats.syntax.functor.*
import cats.syntax.flatMap.*
object FuncOpSem {
def restrictStreamsInScope[S[_], Alg[_]: Monad](
tree: RawTag.Tree
)(using N: NamesAlgebra[S, Alg]): Alg[RawTag.Tree] = N
.streamsDefinedWithinScope()
.map(streams =>
streams.toList
.foldLeft(tree) { case (tree, (streamName, streamType)) =>
RestrictionTag(streamName, streamType).wrap(tree)
}
)
}

View File

@ -1,24 +1,24 @@
package aqua.semantics.expr.func
import aqua.raw.ops.{FuncOp, IfTag}
import aqua.parser.expr.func.IfExpr
import aqua.raw.value.ValueRaw
import aqua.raw.Raw
import aqua.raw.ops.{FuncOp, IfTag}
import aqua.raw.value.ValueRaw
import aqua.semantics.Prog
import aqua.semantics.rules.ValuesAlgebra
import aqua.semantics.rules.abilities.AbilitiesAlgebra
import aqua.semantics.rules.locations.LocationsAlgebra
import aqua.semantics.rules.names.NamesAlgebra
import aqua.semantics.rules.types.TypesAlgebra
import aqua.types.ScalarType
import aqua.types.Type
import cats.Monad
import cats.syntax.applicative.*
import cats.syntax.apply.*
import cats.syntax.flatMap.*
import cats.syntax.functor.*
import cats.syntax.apply.*
import cats.syntax.traverse.*
import aqua.types.ScalarType
class IfSem[S[_]](val expr: IfExpr[S]) extends AnyVal {
@ -46,10 +46,7 @@ class IfSem[S[_]](val expr: IfExpr[S]) extends AnyVal {
(value, ops: Raw) =>
(value, ops) match {
case (Some(vr), FuncOp(op)) =>
for {
restricted <- FuncOpSem.restrictStreamsInScope(op)
tag = IfTag(vr).wrap(restricted)
} yield tag.toFuncOp
IfTag(vr).wrap(op).toFuncOp.pure
case (None, _) => Raw.error("`if` expression errored in matching types").pure
case _ => Raw.error("Wrong body of the `if` expression").pure
}

View File

@ -56,26 +56,26 @@ class ParSeqSem[S[_]](val expr: ParSeqExpr[S]) extends AnyVal {
): F[Raw] =
V.valueToRaw(expr.peerId).map((_, iterableVM, ops)).flatMap {
case (Some(peerId), Some(vm), FuncOp(op)) =>
for {
restricted <- FuncOpSem.restrictStreamsInScope(op)
onTag = OnTag(
val onTag = OnTag(
peerId = peerId,
via = Chain.fromSeq(viaVM),
strategy = OnTag.ReturnStrategy.Relay.some
)
/**
* `parseq` => par (`never` as `last` in `fold`)
* So that peer initiating `parseq` would not continue execution past it
*/
tag = ForTag
ForTag
.par(expr.item.value, vm)
.wrap(
ParTag.wrap(
onTag.wrap(restricted),
onTag.wrap(op),
NextTag(expr.item.value).leaf
)
)
} yield tag.toFuncOp
.toFuncOp
.pure
case (None, _, _) => Raw.error("ParSeqSem: could not resolve `peerId`").pure
case (_, None, _) => Raw.error("ParSeqSem: could not resolve `iterable`").pure
case (_, _, _) => Raw.error("ParSeqSem: wrong body of `parseq` block").pure

View File

@ -1,8 +1,8 @@
package aqua.semantics.expr.func
import aqua.raw.ops.{FuncOp, TryTag}
import aqua.parser.expr.func.TryExpr
import aqua.raw.Raw
import aqua.raw.ops.{FuncOp, TryTag}
import aqua.semantics.Prog
import aqua.semantics.rules.ValuesAlgebra
import aqua.semantics.rules.abilities.AbilitiesAlgebra
@ -10,9 +10,9 @@ import aqua.semantics.rules.locations.LocationsAlgebra
import aqua.semantics.rules.names.NamesAlgebra
import aqua.semantics.rules.types.TypesAlgebra
import cats.Monad
import cats.syntax.applicative.*
import cats.syntax.functor.*
import cats.Monad
class TrySem[S[_]](val expr: TryExpr[S]) extends AnyVal {
@ -27,10 +27,7 @@ class TrySem[S[_]](val expr: TryExpr[S]) extends AnyVal {
.after((ops: Raw) =>
ops match {
case FuncOp(op) =>
for {
restricted <- FuncOpSem.restrictStreamsInScope(op)
tag = TryTag.wrap(restricted)
} yield tag.toFuncOp
TryTag.wrap(op).toFuncOp.pure
case _ =>
Raw.error("Wrong body of the `try` expression").pure[Alg]
}

View File

@ -2,6 +2,7 @@ package aqua.semantics.rules.names
import aqua.parser.lexer.{LiteralToken, Name, Token, ValueToken}
import aqua.types.{ArrowType, StreamType, Type}
import cats.InjectK
trait NamesAlgebra[S[_], Alg[_]] {
@ -25,8 +26,6 @@ trait NamesAlgebra[S[_], Alg[_]] {
def defineArrow(name: Name[S], gen: ArrowType, isRoot: Boolean): Alg[Boolean]
def streamsDefinedWithinScope(): Alg[Map[String, StreamType]]
def beginScope(token: Token[S]): Alg[Unit]
def endScope(): Alg[Unit]

View File

@ -168,13 +168,6 @@ class NamesInterpreter[S[_], X](using
)
}
override def streamsDefinedWithinScope(): SX[Map[String, StreamType]] =
mapStackHead(Map.empty) { frame =>
frame -> frame.names.collect { case (n, st @ StreamType(_)) =>
n -> st
}
}
override def beginScope(token: Token[S]): SX[Unit] =
stackInt.beginScope(NamesState.Frame(token))

View File

@ -142,7 +142,6 @@ class SemanticsSpec extends AnyFlatSpec with Matchers with Inside {
val streamType = StreamType(ScalarType.string)
val stream = VarRaw(name, streamType)
RestrictionTag(stream.name, streamType).wrap(
SeqTag.wrap(
DeclareStreamTag(stream).leaf,
PushToStreamTag(
@ -150,7 +149,6 @@ class SemanticsSpec extends AnyFlatSpec with Matchers with Inside {
Call.Export(name, streamType)
).leaf
)
)
}
// use it to fix https://github.com/fluencelabs/aqua/issues/90