#321 restricted stream scopes (#359)

This commit is contained in:
Dmitry Kurinskiy 2021-11-17 08:25:06 +03:00 committed by GitHub
parent 3665abe4b7
commit 27b885f12d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 331 additions and 70 deletions

53
aqua-src/restrict.aqua Normal file
View 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

View File

@ -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"

View File

@ -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)
}

View File

@ -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(

View File

@ -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,

View File

@ -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

View File

@ -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
}
)

View File

@ -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

View File

@ -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)
)
)
}
}

View File

@ -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)
}

View File

@ -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("")(" " + _)}"
}

View File

@ -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))
}

View File

@ -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,

View File

@ -54,6 +54,7 @@ object Topology extends Logging {
i = i + 1
i
}
val resolvedCofree = cursor
.cata(wrap) { rc =>
logger.debug(s"<:> $rc")

View File

@ -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],

View File

@ -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)
}

View File

@ -21,5 +21,7 @@ trait NamesAlgebra[S[_], Alg[_]] {
def beginScope(token: Token[S]): Alg[Unit]
def streamsDefinedWithinScope(): Alg[Set[String]]
def endScope(): Alg[Unit]
}

View File

@ -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
}

View File

@ -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"
}