mirror of
https://github.com/fluencelabs/aqua.git
synced 2024-12-04 14:40:17 +00:00
parent
3665abe4b7
commit
27b885f12d
53
aqua-src/restrict.aqua
Normal file
53
aqua-src/restrict.aqua
Normal file
@ -0,0 +1,53 @@
|
||||
module Restrict
|
||||
|
||||
export withLoop, buildOptUsage, checkKeepReturn, checkKeepArg, retrieve_records
|
||||
|
||||
|
||||
func withLoop(xs: []string):
|
||||
for x <- xs:
|
||||
s: *string
|
||||
s <<- x
|
||||
|
||||
func buildOpt() -> ?string:
|
||||
s: *string
|
||||
s <<- "none"
|
||||
<- s
|
||||
|
||||
func buildOptUsage():
|
||||
a <- buildOpt()
|
||||
b <- buildOpt()
|
||||
for x <- b:
|
||||
z <- buildOpt()
|
||||
|
||||
func keepReturn() -> *string:
|
||||
s: *string
|
||||
s <<- "should be not restricted"
|
||||
<- s
|
||||
|
||||
func checkKeepReturn() -> []string:
|
||||
s <- keepReturn()
|
||||
s <<- "and more"
|
||||
<- s
|
||||
|
||||
func keepArg(arg: *string) -> []string:
|
||||
arg <<- "push more"
|
||||
<- arg
|
||||
|
||||
func checkKeepArg() -> []string, []string:
|
||||
a: *string
|
||||
keepArg(a)
|
||||
y <- keepArg(a)
|
||||
a <<- "more"
|
||||
<- a, y
|
||||
|
||||
-- failing Aqua code:
|
||||
service TestService("test-service"):
|
||||
get_records(key: string) -> []string
|
||||
|
||||
func append_records(peer: string, srum: *[]string):
|
||||
srum <- TestService.get_records(peer)
|
||||
|
||||
func retrieve_records(peer: string) -> [][]string:
|
||||
records: *[]string
|
||||
append_records(peer, records)
|
||||
<- records
|
@ -10,6 +10,7 @@ object Keyword {
|
||||
|
||||
case object Null extends Keyword("null")
|
||||
|
||||
case object New extends Keyword("new")
|
||||
case object Next extends Keyword("next")
|
||||
|
||||
case object Fold extends Keyword("fold")
|
||||
@ -78,6 +79,8 @@ object Air {
|
||||
|
||||
case object Null extends Air(Keyword.Null)
|
||||
|
||||
case class New(item: DataView, instruction: Air) extends Air(Keyword.New)
|
||||
|
||||
case class Next(label: String) extends Air(Keyword.Next)
|
||||
|
||||
case class Fold(iterable: DataView, label: String, instruction: Air) extends Air(Keyword.Fold)
|
||||
@ -114,6 +117,7 @@ object Air {
|
||||
(air match {
|
||||
case Air.Null ⇒ ""
|
||||
case Air.Next(label) ⇒ s" $label"
|
||||
case Air.New(item, inst) ⇒ s" ${item.show}\n${showNext(inst)}$space"
|
||||
case Air.Fold(iter, label, inst) ⇒ s" ${iter.show} $label\n${showNext(inst)}$space"
|
||||
case Air.Match(left, right, inst) ⇒
|
||||
s" ${left.show} ${right.show}\n${showNext(inst)}$space"
|
||||
|
@ -85,6 +85,8 @@ object AirGen extends Logging {
|
||||
|
||||
case FoldRes(item, iterable) =>
|
||||
Eval later ForGen(valueToData(iterable), item, opsToSingle(ops))
|
||||
case RestrictionRes(item, isStream) =>
|
||||
Eval later NewGen(item, isStream, opsToSingle(ops))
|
||||
case CallServiceRes(serviceId, funcName, CallRes(args, exportTo), peerId) =>
|
||||
Eval.later(
|
||||
ServiceCallGen(
|
||||
@ -151,6 +153,12 @@ case class ForGen(iterable: DataView, item: String, body: AirGen) extends AirGen
|
||||
override def generate: Air = Air.Fold(iterable, item, body.generate)
|
||||
}
|
||||
|
||||
case class NewGen(item: String, isStream: Boolean, body: AirGen) extends AirGen {
|
||||
|
||||
override def generate: Air =
|
||||
Air.New(if (isStream) DataView.Stream("$" + item) else DataView.Variable(item), body.generate)
|
||||
}
|
||||
|
||||
case class NextGen(item: String) extends AirGen {
|
||||
override def generate: Air = Air.Next(item)
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ val circeVersion = "0.14.1"
|
||||
name := "aqua-hll"
|
||||
|
||||
val commons = Seq(
|
||||
baseAquaVersion := "0.4.1",
|
||||
baseAquaVersion := "0.5.0",
|
||||
version := baseAquaVersion.value + "-" + sys.env.getOrElse("BUILD_NUMBER", "SNAPSHOT"),
|
||||
scalaVersion := dottyVersion,
|
||||
libraryDependencies ++= Seq(
|
||||
|
@ -22,7 +22,7 @@ object Test extends IOApp.Simple {
|
||||
start <- IO(System.currentTimeMillis())
|
||||
_ <- AquaPathCompiler
|
||||
.compileFilesTo[IO](
|
||||
Path("./aqua-src/closure.aqua"),
|
||||
Path("./aqua-src/restrict.aqua"),
|
||||
List(Path("./aqua")),
|
||||
Option(Path("./target")),
|
||||
TypeScriptBackend,
|
||||
|
@ -80,7 +80,10 @@ case class FuncCallable(
|
||||
// Function body on its own defines some values; collect their names
|
||||
// except stream arguments. They should be already renamed
|
||||
val treeDefines =
|
||||
treeWithValues.definesVarNames.value -- streamArgs.keySet -- call.exportTo.filter { exp =>
|
||||
treeWithValues.definesVarNames.value -- streamArgs.keySet -- streamArgs.values.collect {
|
||||
case VarModel(streamNameWasAlreadySubstitutedAbove, _, _) =>
|
||||
streamNameWasAlreadySubstitutedAbove
|
||||
} -- call.exportTo.filter { exp =>
|
||||
exp.`type` match {
|
||||
case StreamType(_) => false
|
||||
case _ => true
|
||||
|
@ -32,6 +32,8 @@ case class FuncOp(tree: Cofree[Chain, RawTag]) extends Model {
|
||||
case (CallServiceTag(_, _, Call(_, exportTo)), acc) if exportTo.nonEmpty =>
|
||||
Eval.later(acc.foldLeft(exportTo.map(_.name).toSet)(_ ++ _))
|
||||
case (NextTag(exportTo), acc) => Eval.later(acc.foldLeft(Set(exportTo))(_ ++ _))
|
||||
case (RestrictionTag(name, _), acc) =>
|
||||
Eval.later(acc.foldLeft(Set(name))(_ ++ _))
|
||||
case (_, acc) => Eval.later(acc.foldLeft(Set.empty[String])(_ ++ _))
|
||||
}
|
||||
|
||||
@ -42,6 +44,8 @@ case class FuncOp(tree: Cofree[Chain, RawTag]) extends Model {
|
||||
Eval.later(acc.foldLeft(exportTo.map(_.name).toSet)(_ ++ _))
|
||||
case (PushToStreamTag(_, exportTo), acc) =>
|
||||
Eval.later(acc.foldLeft(Set(exportTo.name))(_ ++ _))
|
||||
case (RestrictionTag(name, _), acc) =>
|
||||
Eval.later(acc.foldLeft(Set.empty[String])(_ ++ _) - name)
|
||||
case (CanonicalizeTag(_, exportTo), acc) =>
|
||||
Eval.later(acc.foldLeft(Set(exportTo.name))(_ ++ _))
|
||||
case (_, acc) => Eval.later(acc.foldLeft(Set.empty[String])(_ ++ _))
|
||||
@ -55,6 +59,8 @@ case class FuncOp(tree: Cofree[Chain, RawTag]) extends Model {
|
||||
Eval.later(acc.foldLeft(call.argVarNames)(_ ++ _))
|
||||
case (PushToStreamTag(operand, _), acc) =>
|
||||
Eval.later(acc.foldLeft(ValueModel.varName(operand).toSet)(_ ++ _))
|
||||
case (RestrictionTag(name, _), acc) =>
|
||||
Eval.later(acc.foldLeft(Set.empty[String])(_ ++ _) - name)
|
||||
case (CanonicalizeTag(operand, _), acc) =>
|
||||
Eval.later(acc.foldLeft(ValueModel.varName(operand).toSet)(_ ++ _))
|
||||
case (MatchMismatchTag(a, b, _), acc) =>
|
||||
@ -86,6 +92,7 @@ case class FuncOp(tree: Cofree[Chain, RawTag]) extends Model {
|
||||
case a: AssignmentTag => a.copy(assignTo = vals.getOrElse(a.assignTo, a.assignTo))
|
||||
case t: ForTag if vals.contains(t.item) => t.copy(item = vals(t.item))
|
||||
case t: NextTag if vals.contains(t.item) => t.copy(item = vals(t.item))
|
||||
case t: RestrictionTag if vals.contains(t.name) => t.copy(name = vals(t.name))
|
||||
case t => t
|
||||
}
|
||||
)
|
||||
|
@ -78,6 +78,7 @@ case class OnTag(peerId: ValueModel, via: Chain[ValueModel]) extends SeqGroupTag
|
||||
s"(on $peerId${if (via.nonEmpty) " via " + via.toList.mkString(" via ") else ""})"
|
||||
}
|
||||
case class NextTag(item: String) extends RawTag
|
||||
case class RestrictionTag(name: String, isStream: Boolean) extends SeqGroupTag
|
||||
|
||||
case class MatchMismatchTag(left: ValueModel, right: ValueModel, shouldMatch: Boolean)
|
||||
extends SeqGroupTag
|
||||
|
@ -4,7 +4,7 @@ import aqua.model.func.Call
|
||||
import aqua.model.func.raw.{FuncOp, FuncOps, OnTag, ReturnTag}
|
||||
import aqua.model.transform.cursor.ChainZipper
|
||||
import aqua.model.{LiteralModel, ValueModel, VarModel}
|
||||
import aqua.types.ScalarType
|
||||
import aqua.types.{ArrayType, ScalarType}
|
||||
import cats.data.{Chain, NonEmptyList}
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
@ -20,7 +20,7 @@ class RawCursorSpec extends AnyFlatSpec with Matchers {
|
||||
onVia(
|
||||
LiteralModel.initPeerId,
|
||||
Chain.empty,
|
||||
callService(LiteralModel.quote("calledOutside"), "fn", Call(Nil, Nil)),
|
||||
callService(LiteralModel.quote("calledOutside"), "fn", Call(Nil, Nil))
|
||||
).tree
|
||||
)
|
||||
)
|
||||
@ -40,9 +40,8 @@ class RawCursorSpec extends AnyFlatSpec with Matchers {
|
||||
callService(LiteralModel.quote("1"), "fn", Call(Nil, Nil)),
|
||||
callService(LiteralModel.quote("2"), "fn", Call(Nil, Nil)),
|
||||
callService(LiteralModel.quote("3"), "fn", Call(Nil, Nil)),
|
||||
callService(LiteralModel.quote("4"), "fn", Call(Nil, Nil)),
|
||||
callService(LiteralModel.quote("4"), "fn", Call(Nil, Nil))
|
||||
)
|
||||
|
||||
).tree
|
||||
)
|
||||
)
|
||||
@ -61,7 +60,7 @@ class RawCursorSpec extends AnyFlatSpec with Matchers {
|
||||
onVia(
|
||||
LiteralModel.initPeerId,
|
||||
Chain.one(VarModel("-relay-", ScalarType.string)),
|
||||
callService(LiteralModel.quote("calledOutside"), "fn", Call(Nil, Nil)),
|
||||
callService(LiteralModel.quote("calledOutside"), "fn", Call(Nil, Nil))
|
||||
).tree
|
||||
)
|
||||
)
|
||||
@ -132,4 +131,89 @@ class RawCursorSpec extends AnyFlatSpec with Matchers {
|
||||
|
||||
}
|
||||
|
||||
"raw cursor" should "move properly with fold" in {
|
||||
|
||||
val raw = RawCursor(
|
||||
NonEmptyList.one(
|
||||
ChainZipper.one(
|
||||
onVia(
|
||||
LiteralModel.initPeerId,
|
||||
Chain.one(VarModel("-relay-", ScalarType.string)),
|
||||
seq(
|
||||
callService(LiteralModel.quote("calledOutside"), "fn", Call(Nil, Nil)),
|
||||
onVia(
|
||||
VarModel("-other-", ScalarType.string),
|
||||
Chain.one(VarModel("-external-", ScalarType.string)),
|
||||
fold(
|
||||
"item",
|
||||
VarModel("iterable", ArrayType(ScalarType.string)),
|
||||
onVia(
|
||||
VarModel("-in-fold-", ScalarType.string),
|
||||
Chain.one(VarModel("-fold-relay-", ScalarType.string)),
|
||||
callService(
|
||||
LiteralModel.quote("calledInside"),
|
||||
"fn",
|
||||
Call(Nil, Call.Export("export", ScalarType.string) :: Nil)
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
callService(
|
||||
LiteralModel.quote("return"),
|
||||
"fn",
|
||||
Call(VarModel("export", ScalarType.string) :: Nil, Nil)
|
||||
)
|
||||
)
|
||||
).tree
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
raw.tag should be(
|
||||
OnTag(LiteralModel.initPeerId, Chain.one(VarModel("-relay-", ScalarType.string)))
|
||||
)
|
||||
raw.firstExecuted.map(_.tag) should be(
|
||||
Some(
|
||||
callService(LiteralModel.quote("calledOutside"), "fn", Call(Nil, Nil)).tree.head
|
||||
)
|
||||
)
|
||||
raw.lastExecuted.map(_.tag) should be(
|
||||
Some(
|
||||
callService(
|
||||
LiteralModel.quote("return"),
|
||||
"fn",
|
||||
Call(VarModel("export", ScalarType.string) :: Nil, Nil)
|
||||
).tree.head
|
||||
)
|
||||
)
|
||||
raw.lastExecuted.flatMap(_.seqPrev).flatMap(_.lastExecuted).map(_.tag) should be(
|
||||
Some(
|
||||
callService(
|
||||
LiteralModel.quote("calledInside"),
|
||||
"fn",
|
||||
Call(Nil, Call.Export("export", ScalarType.string) :: Nil)
|
||||
).tree.head
|
||||
)
|
||||
)
|
||||
raw.lastExecuted.flatMap(_.seqPrev).map(_.pathOn).get should be(
|
||||
OnTag(
|
||||
VarModel("-in-fold-", ScalarType.string),
|
||||
Chain.one(VarModel("-fold-relay-", ScalarType.string))
|
||||
) :: OnTag(
|
||||
VarModel("-other-", ScalarType.string),
|
||||
Chain.one(VarModel("-external-", ScalarType.string))
|
||||
) :: OnTag(
|
||||
LiteralModel.initPeerId,
|
||||
Chain.one(VarModel("-relay-", ScalarType.string))
|
||||
) :: Nil
|
||||
)
|
||||
raw.lastExecuted.map(_.pathFromPrev).get should be(
|
||||
Chain(
|
||||
VarModel("-fold-relay-", ScalarType.string),
|
||||
VarModel("-external-", ScalarType.string),
|
||||
VarModel("-relay-", ScalarType.string)
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -440,7 +440,7 @@ class TopologySpec extends AnyFlatSpec with Matchers {
|
||||
// this example doesn't create a hop on relay after fold
|
||||
// but the test create it, so there is not a one-on-one simulation
|
||||
// change it or write an integration test
|
||||
"topology resolver" should "create returning hops on chain of 'on'" in {
|
||||
"topology resolver" should "create returning hops on chain of 'on'" ignore {
|
||||
val init =
|
||||
on(
|
||||
initPeer,
|
||||
@ -468,30 +468,31 @@ class TopologySpec extends AnyFlatSpec with Matchers {
|
||||
|
||||
val proc: Node.Res = Topology.resolve(init)
|
||||
|
||||
/*val expected: Node.Res =
|
||||
val expected: Node.Res =
|
||||
MakeRes.seq(
|
||||
callRes(0, initPeer),
|
||||
callRes(1, otherRelay),
|
||||
callRes(1, otherRelay)
|
||||
)
|
||||
proc.equalsOrPrintDiff(expected) should be(true)*/
|
||||
proc.equalsOrPrintDiff(expected) should be(true)
|
||||
}
|
||||
|
||||
// This behavior is correct, but as two seqs are not flattened, it's a question how to make the matching result structure
|
||||
"topology resolver" should "create returning hops on nested 'on'" ignore {
|
||||
val init =
|
||||
on(
|
||||
initPeer,
|
||||
Nil,
|
||||
relay :: Nil,
|
||||
callTag(0),
|
||||
on(
|
||||
otherRelay,
|
||||
otherPeer,
|
||||
Nil,
|
||||
callTag(1),
|
||||
fold(
|
||||
"i",
|
||||
valueArray,
|
||||
on(
|
||||
otherRelay2,
|
||||
otherRelay :: Nil,
|
||||
otherPeer2,
|
||||
otherRelay2 :: Nil,
|
||||
callTag(2)
|
||||
)
|
||||
)
|
||||
@ -509,54 +510,54 @@ class TopologySpec extends AnyFlatSpec with Matchers {
|
||||
"i",
|
||||
valueArray,
|
||||
MakeRes.seq(
|
||||
callRes(2, otherRelay2),
|
||||
through(otherRelay2),
|
||||
callRes(2, otherPeer2),
|
||||
through(otherRelay2),
|
||||
nextRes("i")
|
||||
)
|
||||
),
|
||||
through(otherRelay),
|
||||
through(relay),
|
||||
callRes(3, initPeer)
|
||||
)
|
||||
|
||||
proc.equalsOrPrintDiff(expected) should be(true)
|
||||
}
|
||||
|
||||
// https://github.com/fluencelabs/aqua/issues/205
|
||||
"topology resolver" should "optimize path over fold" ignore {
|
||||
val i = VarModel("i", ScalarType.string)
|
||||
val init = {
|
||||
val init =
|
||||
on(
|
||||
initPeer,
|
||||
Nil,
|
||||
relay :: Nil,
|
||||
fold(
|
||||
"i",
|
||||
valueArray,
|
||||
on(
|
||||
i,
|
||||
relay :: Nil,
|
||||
otherRelay :: Nil,
|
||||
callTag(1)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
val proc = Topology.resolve(init)
|
||||
|
||||
val expected: Node.Res = {
|
||||
val expected: Node.Res =
|
||||
MakeRes.seq(
|
||||
through(relay),
|
||||
through(otherRelay),
|
||||
MakeRes.fold(
|
||||
"i",
|
||||
valueArray,
|
||||
MakeRes.seq(
|
||||
callRes(1, i),
|
||||
through(relay),
|
||||
through(otherRelay),
|
||||
nextRes("i")
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
proc.equalsOrPrintDiff(expected) should be(true)
|
||||
}
|
||||
|
||||
|
@ -3,4 +3,6 @@ package aqua.model.transform.res
|
||||
import aqua.model.ValueModel
|
||||
import aqua.model.func.Call
|
||||
|
||||
case class CallRes(args: List[ValueModel], exportTo: Option[Call.Export])
|
||||
case class CallRes(args: List[ValueModel], exportTo: Option[Call.Export]) {
|
||||
override def toString: String = s"[${args.mkString(" ")}]${exportTo.fold("")(" " + _)}"
|
||||
}
|
||||
|
@ -17,6 +17,9 @@ object MakeRes {
|
||||
def next(item: String): Res =
|
||||
leaf(NextRes(item))
|
||||
|
||||
def wrap(op: ResolvedOp, internal: Res): Res =
|
||||
Cofree[Chain, ResolvedOp](op, Eval.now(Chain.one(internal)))
|
||||
|
||||
def seq(first: Res, second: Res, more: Res*): Res =
|
||||
Cofree[Chain, ResolvedOp](SeqRes, Eval.later(first +: second +: Chain.fromSeq(more)))
|
||||
|
||||
@ -50,6 +53,7 @@ object MakeRes {
|
||||
case _: OnTag => leaf(SeqRes)
|
||||
case MatchMismatchTag(a, b, s) => leaf(MatchMismatchRes(a, b, s))
|
||||
case ForTag(item, iter) => leaf(FoldRes(item, iter))
|
||||
case RestrictionTag(item, isStream) => leaf(RestrictionRes(item, isStream))
|
||||
case ParTag | ParTag.Detach => leaf(ParRes)
|
||||
case XorTag | XorTag.LeftBiased => leaf(XorRes)
|
||||
case NextTag(item) => leaf(NextRes(item))
|
||||
@ -57,6 +61,8 @@ object MakeRes {
|
||||
operand.`type` match {
|
||||
case StreamType(st) =>
|
||||
val tmpName = s"push-to-stream-$i"
|
||||
// wrap (
|
||||
// RestrictionRes(tmpName, isStream = false),
|
||||
seq(
|
||||
canon(
|
||||
currentPeerId
|
||||
@ -66,6 +72,7 @@ object MakeRes {
|
||||
),
|
||||
leaf(ApRes(VarModel(tmpName, ArrayType(st)), exportTo))
|
||||
)
|
||||
// )
|
||||
case _ =>
|
||||
leaf(ApRes(operand, exportTo))
|
||||
}
|
||||
|
@ -11,13 +11,22 @@ case object SeqRes extends ResolvedOp
|
||||
case object ParRes extends ResolvedOp
|
||||
case object XorRes extends ResolvedOp
|
||||
|
||||
case class NextRes(item: String) extends ResolvedOp
|
||||
case class NextRes(item: String) extends ResolvedOp {
|
||||
override def toString: String = s"(next $item)"
|
||||
}
|
||||
|
||||
case class MatchMismatchRes(left: ValueModel, right: ValueModel, shouldMatch: Boolean)
|
||||
extends ResolvedOp {
|
||||
override def toString: String = s"(${if (shouldMatch) "match" else "mismatch"} $left $right)"
|
||||
}
|
||||
case class FoldRes(item: String, iterable: ValueModel) extends ResolvedOp
|
||||
|
||||
case class FoldRes(item: String, iterable: ValueModel) extends ResolvedOp {
|
||||
override def toString: String = s"(fold $iterable $item "
|
||||
}
|
||||
|
||||
case class RestrictionRes(item: String, isStream: Boolean) extends ResolvedOp {
|
||||
override def toString: String = s"(new ${if (isStream) "$" else ""}$item "
|
||||
}
|
||||
|
||||
case class AbilityIdRes(
|
||||
value: ValueModel,
|
||||
|
@ -54,6 +54,7 @@ object Topology extends Logging {
|
||||
i = i + 1
|
||||
i
|
||||
}
|
||||
|
||||
val resolvedCofree = cursor
|
||||
.cata(wrap) { rc =>
|
||||
logger.debug(s"<:> $rc")
|
||||
|
@ -1,8 +1,9 @@
|
||||
package aqua.semantics.expr.func
|
||||
|
||||
import aqua.model.func.ArrowModel
|
||||
import aqua.model.func.raw.{FuncOp, FuncOps, ReturnTag, SeqTag}
|
||||
import aqua.model.{Model, ValueModel}
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.func.raw.{FuncOp, FuncOps, RestrictionTag, ReturnTag, SeqTag}
|
||||
import aqua.model.{Model, ValueModel, VarModel}
|
||||
import aqua.parser.expr.FuncExpr
|
||||
import aqua.parser.expr.func.ArrowExpr
|
||||
import aqua.parser.lexer.{Arg, DataTypeToken}
|
||||
@ -11,7 +12,7 @@ import aqua.semantics.rules.ValuesAlgebra
|
||||
import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
||||
import aqua.semantics.rules.names.NamesAlgebra
|
||||
import aqua.semantics.rules.types.TypesAlgebra
|
||||
import aqua.types.{ArrowType, ProductType, Type}
|
||||
import aqua.types.{ArrowType, ProductType, StreamType, Type}
|
||||
import cats.data.{Chain, NonEmptyList}
|
||||
import cats.free.{Cofree, Free}
|
||||
import cats.syntax.applicative.*
|
||||
@ -37,7 +38,8 @@ class ArrowSem[S[_]](val expr: ArrowExpr[S]) extends AnyVal {
|
||||
)
|
||||
.flatMap((arrowType: ArrowType) =>
|
||||
// Create local variables
|
||||
expr.arrowTypeExpr.args.flatMap(_._1)
|
||||
expr.arrowTypeExpr.args
|
||||
.flatMap(_._1)
|
||||
.zip(
|
||||
arrowType.domain.toList
|
||||
)
|
||||
@ -55,15 +57,60 @@ class ArrowSem[S[_]](val expr: ArrowExpr[S]) extends AnyVal {
|
||||
N: NamesAlgebra[S, Alg],
|
||||
A: AbilitiesAlgebra[S, Alg]
|
||||
): Alg[Model] =
|
||||
A.endScope() *> N.endScope() *> T.endArrowScope(expr.arrowTypeExpr).map { retValues =>
|
||||
bodyGen match {
|
||||
case m: FuncOp =>
|
||||
// TODO: wrap with local on...via...
|
||||
ArrowModel(funcArrow, retValues, m)
|
||||
case m =>
|
||||
m
|
||||
}
|
||||
}
|
||||
A.endScope() *> (N.streamsDefinedWithinScope(), T.endArrowScope(expr.arrowTypeExpr)).mapN {
|
||||
(streams, retValues) =>
|
||||
bodyGen match {
|
||||
case m: FuncOp =>
|
||||
// TODO: wrap with local on...via...
|
||||
|
||||
// These streams are returned as streams
|
||||
val retStreams: Map[String, Option[Type]] =
|
||||
(retValues zip funcArrow.codomain.toList).collect {
|
||||
case (VarModel(n, StreamType(_), _), StreamType(_)) => n -> None
|
||||
case (VarModel(n, StreamType(_), _), t) => n -> Some(t)
|
||||
}.toMap
|
||||
|
||||
val builtStreams = retStreams.collect { case (n, Some(t)) =>
|
||||
n -> t
|
||||
}
|
||||
val escapingStreams = retStreams.collect { case (n, None) =>
|
||||
n
|
||||
}
|
||||
|
||||
// Remove stream arguments, and values returned as streams
|
||||
val localStreams = streams -- funcArrow.domain.labelledData.map(_._1) -- escapingStreams
|
||||
|
||||
// Restrict all the local streams
|
||||
val (body, retValuesFix) = localStreams.foldLeft((m, retValues)) { case ((b, rs), n) =>
|
||||
if (
|
||||
rs.exists {
|
||||
case VarModel(`n`, _, _) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
FuncOp.wrap(
|
||||
RestrictionTag(n, isStream = true),
|
||||
FuncOps.seq(
|
||||
b :: rs.collect { case vn @ VarModel(`n`, _, _) =>
|
||||
FuncOps.canonicalize(
|
||||
vn,
|
||||
Call.Export(s"$n-fix", builtStreams.getOrElse(n, vn.lastType))
|
||||
)
|
||||
}: _*
|
||||
)
|
||||
) -> rs.map {
|
||||
case vn @ VarModel(`n`, _, _) =>
|
||||
VarModel(s"$n-fix", builtStreams.getOrElse(n, vn.lastType))
|
||||
case vm => vm
|
||||
}
|
||||
else FuncOp.wrap(RestrictionTag(n, isStream = true), b) -> rs
|
||||
}
|
||||
|
||||
ArrowModel(funcArrow, retValuesFix, body)
|
||||
case m =>
|
||||
m
|
||||
}
|
||||
} <* N.endScope()
|
||||
|
||||
def program[Alg[_]: Monad](implicit
|
||||
T: TypesAlgebra[S, Alg],
|
||||
|
@ -12,17 +12,18 @@ import aqua.types.{ArrayType, BoxType}
|
||||
import cats.Monad
|
||||
import cats.data.Chain
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.apply.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
|
||||
class ForSem[S[_]](val expr: ForExpr[S]) extends AnyVal {
|
||||
|
||||
def program[Alg[_]: Monad](implicit
|
||||
V: ValuesAlgebra[S, Alg],
|
||||
N: NamesAlgebra[S, Alg],
|
||||
T: TypesAlgebra[S, Alg],
|
||||
A: AbilitiesAlgebra[S, Alg]
|
||||
): Prog[Alg, Model] =
|
||||
def program[F[_]: Monad](implicit
|
||||
V: ValuesAlgebra[S, F],
|
||||
N: NamesAlgebra[S, F],
|
||||
T: TypesAlgebra[S, F],
|
||||
A: AbilitiesAlgebra[S, F]
|
||||
): Prog[F, Model] =
|
||||
Prog
|
||||
.around(
|
||||
N.beginScope(expr.item) >> V.valueToModel(expr.iterable).flatMap[Option[ValueModel]] {
|
||||
@ -34,30 +35,44 @@ class ForSem[S[_]](val expr: ForExpr[S]) extends AnyVal {
|
||||
T.ensureTypeMatches(expr.iterable, ArrayType(dt), dt).as(Option.empty[ValueModel])
|
||||
}
|
||||
|
||||
case _ => None.pure[Alg]
|
||||
case _ => None.pure[F]
|
||||
},
|
||||
(stOpt: Option[ValueModel], ops: Model) =>
|
||||
N.endScope() as ((stOpt, ops) match {
|
||||
case (Some(vm), op: FuncOp) =>
|
||||
// Fix: continue execution after fold par immediately, without finding a path out from par branches
|
||||
val innerTag = expr.mode.map(_._2).fold[RawTag](SeqTag) {
|
||||
case ForExpr.ParMode => ParTag
|
||||
case ForExpr.TryMode => XorTag
|
||||
}
|
||||
val forTag = FuncOp.wrap(
|
||||
ForTag(expr.item.value, vm),
|
||||
FuncOp.node(
|
||||
expr.mode.map(_._2).fold[RawTag](SeqTag) {
|
||||
N.streamsDefinedWithinScope()
|
||||
.map((streams: Set[String]) =>
|
||||
(stOpt, ops) match {
|
||||
case (Some(vm), op: FuncOp) =>
|
||||
val innerTag = expr.mode.map(_._2).fold[RawTag](SeqTag) {
|
||||
case ForExpr.ParMode => ParTag
|
||||
case ForExpr.TryMode => XorTag
|
||||
},
|
||||
Chain(op, FuncOp.leaf(NextTag(expr.item.value)))
|
||||
)
|
||||
)
|
||||
if (innerTag == ParTag) FuncOp.node(ParTag, Chain(forTag, FuncOps.empty))
|
||||
else forTag
|
||||
case _ => Model.error("Wrong body of For expr")
|
||||
})
|
||||
}
|
||||
|
||||
// Restrict the streams created within this scope
|
||||
val body =
|
||||
Chain(
|
||||
streams.toList.foldLeft(op) { case (b, streamName) =>
|
||||
FuncOp.wrap(RestrictionTag(streamName, isStream = true), b)
|
||||
},
|
||||
FuncOp.leaf(NextTag(expr.item.value))
|
||||
)
|
||||
|
||||
val forTag = FuncOp.wrap(
|
||||
ForTag(expr.item.value, vm),
|
||||
FuncOp.node(
|
||||
expr.mode.map(_._2).fold[RawTag](SeqTag) {
|
||||
case ForExpr.ParMode => ParTag
|
||||
case ForExpr.TryMode => XorTag
|
||||
},
|
||||
body
|
||||
)
|
||||
)
|
||||
// Fix: continue execution after fold par immediately, without finding a path out from par branches
|
||||
if (innerTag == ParTag) FuncOp.node(ParTag, Chain(forTag, FuncOps.empty))
|
||||
else forTag
|
||||
case _ =>
|
||||
Model.error("Wrong body of the For expression")
|
||||
}
|
||||
) <* N.endScope()
|
||||
)
|
||||
.abilitiesScope[S](expr.token)
|
||||
}
|
||||
|
@ -21,5 +21,7 @@ trait NamesAlgebra[S[_], Alg[_]] {
|
||||
|
||||
def beginScope(token: Token[S]): Alg[Unit]
|
||||
|
||||
def streamsDefinedWithinScope(): Alg[Set[String]]
|
||||
|
||||
def endScope(): Alg[Unit]
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ package aqua.semantics.rules.names
|
||||
import aqua.parser.lexer.{Name, Token}
|
||||
import aqua.semantics.Levenshtein
|
||||
import aqua.semantics.rules.{ReportError, StackInterpreter}
|
||||
import aqua.types.{ArrowType, Type}
|
||||
import aqua.types.{ArrowType, StreamType, Type}
|
||||
import cats.data.{OptionT, State}
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
@ -128,6 +128,13 @@ class NamesInterpreter[S[_], X](implicit lens: Lens[X, NamesState[S]], error: Re
|
||||
override def beginScope(token: Token[S]): SX[Unit] =
|
||||
stackInt.beginScope(NamesState.Frame(token))
|
||||
|
||||
override def streamsDefinedWithinScope(): SX[Set[String]] =
|
||||
stackInt.mapStackHead(State.pure(Set.empty[String])) { frame =>
|
||||
frame -> frame.names.collect { case (n, StreamType(_)) =>
|
||||
n
|
||||
}.toSet
|
||||
}
|
||||
|
||||
override def endScope(): SX[Unit] = stackInt.endScope
|
||||
|
||||
}
|
||||
|
@ -162,14 +162,21 @@ object LiteralType {
|
||||
}
|
||||
|
||||
sealed trait BoxType extends DataType {
|
||||
def isStream: Boolean
|
||||
def element: Type
|
||||
}
|
||||
|
||||
case class ArrayType(element: Type) extends BoxType {
|
||||
|
||||
override def isStream: Boolean = false
|
||||
|
||||
override def toString: String = "[]" + element
|
||||
}
|
||||
|
||||
case class OptionType(element: Type) extends BoxType {
|
||||
|
||||
override def isStream: Boolean = false
|
||||
|
||||
override def toString: String = "?" + element
|
||||
}
|
||||
|
||||
@ -200,6 +207,9 @@ case class ArrowType(domain: ProductType, codomain: ProductType) extends Type {
|
||||
}
|
||||
|
||||
case class StreamType(element: Type) extends BoxType {
|
||||
|
||||
override def isStream: Boolean = true
|
||||
|
||||
override def toString: String = s"*$element"
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user