LNG-86 Stream canonicalization (#553)

This commit is contained in:
Dima 2022-09-30 20:06:55 +04:00 committed by GitHub
parent 3784a71063
commit 95d3dc2d9e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 1488 additions and 1145 deletions

View File

@ -1,44 +1,43 @@
module Argh
data Record:
relay_id: []string
peer_id: string
export test4
-- func bugLng79(arr: *Record) -> u32:
-- stream: *Record
-- for r <- arr:
-- stream <<- r
-- someone = stream[0]
-- on someone.peer_id via someone.relay_id:
-- a = 1 + 1
-- <- a
func test() -> string:
status: *string
status <<- "ok"
stat = status!
service Op1("op"):
array_length(array: []string) -> u32
noop()
--
-- func bugLNG63_3() -> string, u32, []u32:
-- status: *string
-- status <<- "ok"
-- stat = status!
-- num: *u32
-- num <<- 2
-- res = [Op1.array_length(status), num!]
-- <- status!, Op1.array_length(status), [Op1.array_length(status), 3, num!]
<- stat
-- func emptySugar() -> *string:
-- strEmptyStream: *string
-- <- strEmptyStream
service Ser("ser"):
getRecord: -> Record
func test3() -> string, []string:
status: *string
status <<- "ok"
stat = status!
-- func bugLng79(log: string -> ()) -> u32:
-- stream: *Record
-- stream <- Ser.getRecord()
-- someone = stream[0]
-- on someone.peer_id via someone.relay_id:
-- a = 1 + 1
-- <- a
<- stat, status
service Se("se"):
consume: []string -> bool
func test2() -> string, []string, []string:
status: *string
status <<- "ok"
stat = status!
<- stat, status, [status!, status!]
func test4() -> string, bool, []bool:
status: *string
status <<- "ok"
stat = status!
<- status!, Se.consume(status), [Se.consume(status), true]
func returnCanStream() -> string:
status: *string
status <<- "ok"
stat = status!
<- stat
func bugLNG63() -> string:
res <- returnCanStream()
<- res
func emptySugar(arr: []Record) -> u32:
<- arr[1].relay_id.length

View File

@ -23,6 +23,8 @@ object Keyword {
case object Ap extends Keyword("ap")
case object Canon extends Keyword("canon")
case object Seq extends Keyword("seq")
case object Par extends Keyword("par")
@ -45,7 +47,7 @@ object DataView {
case class Stream(name: String) extends DataView
case class VarLens(name: String, lens: String) extends DataView {
case class VarLens(name: String, lens: String, isField: Boolean = true) extends DataView {
def append(sublens: String): VarLens = copy(lens = lens + sublens)
}
@ -55,7 +57,9 @@ object DataView {
case LastError "%last_error%"
case Variable(name) name
case Stream(name) name
case VarLens(name, lens) name + ".$" + lens + "!"
case VarLens(name, lens, isField)
if (isField) name + ".$" + lens + "!"
else name + lens
}
}
@ -101,6 +105,8 @@ object Air {
case class Ap(op: DataView, result: String) extends Air(Keyword.Ap)
case class Canon(op: DataView, peerId: DataView, result: String) extends Air(Keyword.Canon)
case class Comment(comment: String, air: Air) extends Air(Keyword.NA)
private def show(depth: Int, air: Air): String = {
@ -129,6 +135,7 @@ object Air {
case Air.Call(triplet, args, res)
s" ${triplet.show} [${args.map(_.show).mkString(" ")}]${res.fold("")(" " + _)}"
case Air.Ap(operand, result) s" ${operand.show} $result"
case Air.Canon(operand, peerId, result) s" ${peerId.show} ${operand.show} $result"
case Air.Comment(_, _) => ";; Should not be displayed"
}) + ")\n"
}

View File

@ -3,7 +3,7 @@ package aqua.backend.air
import aqua.model.*
import aqua.raw.ops.Call
import aqua.res.*
import aqua.types.StreamType
import aqua.types.{ArrayType, CanonStreamType, StreamType}
import cats.Eval
import cats.data.Chain
import cats.free.Cofree
@ -16,23 +16,32 @@ sealed trait AirGen {
object AirGen extends Logging {
def lambdaToString(ls: List[LambdaModel]): String = ls match {
def propertyToString(ls: List[PropertyModel]): String = ls match {
case Nil => ""
case FunctorModel(field, _) :: tail =>
s".$field${propertyToString(tail)}"
case IntoFieldModel(field, _) :: tail =>
s".$field${lambdaToString(tail)}"
s".$field${propertyToString(tail)}"
case IntoIndexModel(idx, _) :: tail =>
s".[$idx]${lambdaToString(tail)}"
s".[$idx]${propertyToString(tail)}"
}
def valueToData(vm: ValueModel): DataView = vm match {
case LiteralModel(value, _) => DataView.StringScalar(value)
case VarModel(name, t, lambda) =>
case VarModel(name, t, property) =>
val n = (t match {
case _: StreamType => "$" + name
case _: CanonStreamType => "#" + name
case _ => name
}).replace('.', '_')
if (lambda.isEmpty) DataView.Variable(n)
else DataView.VarLens(n, lambdaToString(lambda.toList))
if (property.isEmpty) DataView.Variable(n)
else {
val functors = property.find {
case FunctorModel(_, _) => true
case _ => false
}
DataView.VarLens(n, propertyToString(property.toList), functors.isEmpty)
}
}
def opsToSingle(ops: Chain[AirGen]): AirGen = ops.toList match {
@ -43,6 +52,7 @@ object AirGen extends Logging {
def exportToString(exportTo: CallModel.Export): String = (exportTo match {
case CallModel.Export(name, _: StreamType) => "$" + name
case CallModel.Export(name, _: CanonStreamType) => "#" + name
case CallModel.Export(name, _) => name
}).replace('.', '_')
@ -101,6 +111,11 @@ object AirGen extends Logging {
ApGen(valueToData(operand), exportToString(exportTo))
)
case CanonRes(operand, peerId, exportTo) =>
Eval.later(
CanonGen(valueToData(operand), valueToData(peerId), exportToString(exportTo))
)
case NullRes =>
Eval.now(NullGen)
@ -138,6 +153,12 @@ case class ApGen(operand: DataView, result: String) extends AirGen {
Air.Ap(operand, result)
}
case class CanonGen(operand: DataView, peerId: DataView, result: String) extends AirGen {
override def generate: Air =
Air.Canon(operand, peerId, result)
}
case class MatchMismatchGen(
left: DataView,
right: DataView,

View File

@ -1,6 +1,6 @@
package aqua.compiler
import aqua.model.{CallModel, IntoIndexModel, LiteralModel, ValueModel, VarModel}
import aqua.model.{CallModel, FunctorModel, IntoIndexModel, LiteralModel, ValueModel, VarModel}
import aqua.model.transform.TransformConfig
import aqua.model.transform.Transform
import aqua.parser.ParserError
@ -9,19 +9,9 @@ import aqua.parser.Parser
import aqua.parser.lift.Span
import aqua.parser.lift.Span.S
import aqua.raw.value.{LiteralRaw, ValueRaw, VarRaw}
import aqua.res.{
ApRes,
CallRes,
CallServiceRes,
FoldRes,
MakeRes,
NextRes,
ParRes,
RestrictionRes,
SeqRes
}
import aqua.res.{ApRes, CallRes, CallServiceRes, CanonRes, FoldRes, MakeRes, MatchMismatchRes, NextRes, ParRes, RestrictionRes, SeqRes, XorRes}
import aqua.semantics.lsp.LspContext
import aqua.types.{ArrayType, LiteralType, ScalarType, StreamType, Type}
import aqua.types.{ArrayType, CanonStreamType, LiteralType, ScalarType, StreamType, Type}
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import cats.Id
@ -115,6 +105,36 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers {
).leaf
}
val init = LiteralModel.fromRaw(ValueRaw.InitPeerId)
private def join(vm: VarModel, length: ValueModel) = {
val testVM = VarModel(vm.name + "_test", vm.`type`)
val iter = VarModel("s", ScalarType.string)
val canon = VarModel(vm.name + "_iter_canon", CanonStreamType(ScalarType.string))
val idx = VarModel("incr_idx", ScalarType.u32)
RestrictionRes(testVM.name, true).wrap(
FoldRes(iter.name, vm).wrap(
CallServiceRes(
LiteralModel("\"math\"", ScalarType.string),
"add",
CallRes(
length :: LiteralModel.fromRaw(LiteralRaw.number(1)) :: Nil,
Some(CallModel.Export(idx.name, idx.`type`))
),
init
).leaf,
ApRes(iter, CallModel.Export(testVM.name, testVM.`type`)).leaf,
CanonRes(testVM, init, CallModel.Export(canon.name, canon.`type`)).leaf,
XorRes.wrap(
MatchMismatchRes(canon.copy(properties = Chain.one(FunctorModel("length", ScalarType.u32))), idx, true).leaf,
NextRes(iter.name).leaf
)
),
CanonRes(testVM, init, CallModel.Export(vm.name + "_result_canon", canon.`type`)).leaf,
)
}
"aqua compiler" should "create right topology" in {
val res = compileToContext(
@ -148,7 +168,9 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers {
val peers = VarModel("peers", ArrayType(ScalarType.string))
val peer = VarModel("peer-0", ScalarType.string)
val results = VarModel("results", StreamType(ScalarType.string))
val resultsType = StreamType(ScalarType.string)
val results = VarModel("results", resultsType)
val canonResult = VarModel(results.name + "-fix", CanonStreamType(resultsType.element))
val initPeer = LiteralModel.fromRaw(ValueRaw.InitPeerId)
val expected =
@ -179,31 +201,15 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers {
)
)
),
CallServiceRes(
LiteralModel.fromRaw(LiteralRaw.quote("op")),
"noop",
CallRes(
results.copy(lambda = Chain.one(IntoIndexModel("2", ScalarType.string))) :: Nil,
None
),
initPeer
).leaf,
CallServiceRes(
LiteralModel.fromRaw(LiteralRaw.quote("op")),
"identity",
CallRes(
results :: Nil,
Some(CallModel.Export("results-fix", ArrayType(ScalarType.string)))
),
initPeer
).leaf
join(results, LiteralModel.fromRaw(LiteralRaw.number(2))),
CanonRes(results, init, CallModel.Export(canonResult.name, canonResult.`type`)).leaf
)
),
CallServiceRes(
LiteralModel.fromRaw(LiteralRaw.quote("callbackSrv")),
"response",
CallRes(
VarModel("results-fix", ArrayType(ScalarType.string)) :: Nil,
canonResult :: Nil,
None
),
initPeer
@ -273,6 +279,9 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers {
val Some(funcWrap) = aquaRes.funcs.find(_.funcName == "wrap")
val Some(barfoo) = aquaRes.funcs.find(_.funcName == "barfoo")
val resVM = VarModel("res", StreamType(ScalarType.string))
val resCanonVM = VarModel("res-fix", CanonStreamType(ScalarType.string))
barfoo.body.equalsOrShowDiff(
SeqRes.wrap(
RestrictionRes("res", true).wrap(
@ -288,21 +297,17 @@ class AquaCompilerSpec extends AnyFlatSpec with Matchers {
CallModel.Export("res", StreamType(ScalarType.string))
).leaf,
// canonicalization
CallServiceRes(
LiteralModel.fromRaw(LiteralRaw.quote("op")),
"identity",
CallRes(
VarModel("res", StreamType(ScalarType.string)) :: Nil,
Some(CallModel.Export("res-fix", ArrayType(ScalarType.string)))
),
LiteralModel.fromRaw(ValueRaw.InitPeerId)
CanonRes(
resVM,
LiteralModel.fromRaw(ValueRaw.InitPeerId),
CallModel.Export(resCanonVM.name, resCanonVM.`type`)
).leaf
)
),
CallServiceRes(
LiteralModel.fromRaw(LiteralRaw.quote("callbackSrv")),
"response",
CallRes(VarModel("res-fix", ArrayType(ScalarType.string)) :: Nil, None),
CallRes(resCanonVM :: Nil, None),
LiteralModel.fromRaw(ValueRaw.InitPeerId)
).leaf
)

View File

@ -102,7 +102,7 @@ object ArrowInliner extends Logging {
// collect arguments with stream type
// to exclude it from resolving and rename it with a higher-level stream that passed by argument
// TODO: what if we have streams in lambda???
// TODO: what if we have streams in property???
streamToRename = argsFull.streamArgs.view.mapValues(_.name).toMap
// Find all duplicates in arguments

View File

@ -2,7 +2,7 @@ package aqua.model.inline
import aqua.model.inline.state.{Arrows, Counter, Exports, Mangler}
import aqua.model.*
import aqua.model.inline.raw.{ApplyLambdaRawInliner, CallArrowRawInliner, CollectionRawInliner}
import aqua.model.inline.raw.{ApplyFunctorRawInliner, ApplyPropertiesRawInliner, CallArrowRawInliner, CollectionRawInliner}
import aqua.raw.ops.*
import aqua.raw.value.*
import aqua.types.{ArrayType, OptionType, StreamType}
@ -21,7 +21,7 @@ object RawValueInliner extends Logging {
private[inline] def unfold[S: Mangler: Exports: Arrows](
raw: ValueRaw,
lambdaAllowed: Boolean = true
propertiesAllowed: Boolean = true
): State[S, (ValueModel, Inline)] =
raw match {
case VarRaw(name, t) =>
@ -30,14 +30,17 @@ object RawValueInliner extends Logging {
case LiteralRaw(value, t) =>
State.pure(LiteralModel(value, t) -> Inline.empty)
case alr: ApplyLambdaRaw =>
ApplyLambdaRawInliner(alr, lambdaAllowed)
case alr: ApplyPropertyRaw =>
ApplyPropertiesRawInliner(alr, propertiesAllowed)
case alr: ApplyFunctorRaw =>
ApplyFunctorRawInliner(alr, propertiesAllowed)
case cr: CollectionRaw =>
CollectionRawInliner(cr, lambdaAllowed)
CollectionRawInliner(cr, propertiesAllowed)
case cr: CallArrowRaw =>
CallArrowRawInliner(cr, lambdaAllowed)
CallArrowRawInliner(cr, propertiesAllowed)
case sr: ShadowRaw =>
// First, collect shadowed values
@ -45,7 +48,7 @@ object RawValueInliner extends Logging {
sr.shadowValues.toList
// Unfold/substitute all shadowed value
.traverse { case (name, v) =>
unfold(v, lambdaAllowed).map { case (svm, si) =>
unfold(v, propertiesAllowed).map { case (svm, si) =>
(name, svm, si)
}
}.flatMap { fas =>
@ -59,7 +62,7 @@ object RawValueInliner extends Logging {
.scope(
Exports[S].resolved(res ++ curr.view.mapValues(_.resolveWith(res))) >>
// Resolve the value in the prepared Exports scope
unfold(sr.value, lambdaAllowed)
unfold(sr.value, propertiesAllowed)
)
)
.map { case (vm, inl) =>

View File

@ -5,8 +5,9 @@ import aqua.model.*
import aqua.model.inline.raw.CallArrowRawInliner
import aqua.raw.ops.*
import aqua.raw.value.*
import aqua.types.BoxType
import aqua.types.{ArrayType, BoxType, CanonStreamType, StreamType}
import cats.syntax.traverse.*
import cats.syntax.applicative.*
import cats.instances.list.*
import cats.data.{Chain, State, StateT}
import scribe.{log, Logging}
@ -32,6 +33,66 @@ object TagInliner extends Logging {
private def none[S]: State[S, (Option[OpModel], Option[OpModel.Tree])] =
State.pure(None -> None)
private def fixModel[S: Mangler: Arrows: Exports](
vm: ValueModel,
ops: Option[OpModel.Tree]
): State[S, (ValueModel, Option[OpModel.Tree])] = {
vm match {
case VarModel(name, StreamType(el), l) =>
val canonName = name + "_canon"
Mangler[S].findAndForbidName(canonName).map { n =>
val canon = VarModel(n, CanonStreamType(el), l)
val canonModel = CanonicalizeModel(vm, CallModel.Export(canon.name, canon.`type`)).leaf
canon -> Some(ops.fold(canonModel)(t => SeqModel.wrap(t, canonModel)))
}
case _ => State.pure(vm -> ops)
}
}
private def flat[S: Mangler](vm: ValueModel, op: Option[OpModel.Tree], flatStream: Boolean) = {
vm match {
// flatten stream, because in via we are using `fold`
// and `fold` will hang on stream
case v @ VarModel(n, StreamType(t), l) if flatStream =>
val canonName = v.name + "_canon"
for {
canonN <- Mangler[S].findAndForbidName(canonName)
canonV = VarModel(canonN, CanonStreamType(t), l)
canonOp = CanonicalizeModel(
v.copy(properties = Chain.empty),
CallModel.Export(canonV.name, canonV.`type`)
).leaf
flatResult <- flatCanonStream(canonV, Some(canonOp))
} yield {
val (resV, resOp) = flatResult
(resV, op.fold(resOp)(t => resOp.map(o => SeqModel.wrap(t, o))))
}
case v @ VarModel(_, CanonStreamType(t), _) =>
flatCanonStream(v, op)
case _ => State.pure((vm, op))
}
}
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,
Some(op.fold(apOp)(o => SeqModel.wrap(o, apOp)))
)
}
} else {
State.pure((canonV, op))
}
}
/**
* Processes a single [[RawTag]] that may lead to many changes, including calling [[ArrowInliner]]
*
@ -48,9 +109,12 @@ object TagInliner extends Logging {
for {
peerIdDe <- valueToModel(peerId)
viaDe <- valueListToModel(via.toList)
viaDeFlattened <- viaDe.traverse { case (vm, tree) =>
flat(vm, tree, true)
}
(pid, pif) = peerIdDe
viaD = Chain.fromSeq(viaDe.map(_._1))
viaF = viaDe.flatMap(_._2)
viaD = Chain.fromSeq(viaDeFlattened.map(_._1))
viaF = viaDeFlattened.flatMap(_._2)
} yield Some(OnModel(pid, viaD)) -> parDesugarPrefix(viaF.prependedAll(pif))
@ -58,16 +122,21 @@ object TagInliner extends Logging {
for {
ld <- valueToModel(left)
rd <- valueToModel(right)
} yield Some(MatchMismatchModel(ld._1, rd._1, shouldMatch)) -> parDesugarPrefixOpt(
ld._2,
rd._2
ldfixed <- fixModel(ld._1, ld._2)
rdfixed <- fixModel(rd._1, rd._2)
} yield Some(
MatchMismatchModel(ldfixed._1, rdfixed._1, shouldMatch)
) -> parDesugarPrefixOpt(
ldfixed._2,
rdfixed._2
)
case ForTag(item, iterable) =>
for {
vp <- valueToModel(iterable)
(v, p) = vp
exps <- Exports[S].exports
(vN, pN) = vp
flattened <- flat(vN, pN, true)
(v, p) = flattened
n <- Mangler[S].findAndForbidName(item)
elementType = iterable.`type` match {
case b: BoxType => b.element
@ -94,12 +163,12 @@ object TagInliner extends Logging {
}
case JoinTag(operands) =>
logger.trace("join " + operands)
operands
.traverse(valueToModel)
.traverse(o => valueToModel(o))
.map(nel => {
logger.trace("join after " + nel.map(_._1))
Some(JoinModel(nel.map(_._1))) -> parDesugarPrefix(nel.toList.flatMap(_._2))
// None because join behaviour will be processed in ApplyPropertiesRawInliner
None -> parDesugarPrefix(nel.toList.flatMap(_._2))
})
case CallArrowRawTag(exportTo, value: CallArrowRaw) =>

View File

@ -0,0 +1,40 @@
package aqua.model.inline.raw
import aqua.model.{FlattenModel, FunctorModel, SeqModel, ValueModel, VarModel}
import aqua.model.inline.Inline
import aqua.model.inline.state.{Arrows, Exports, Mangler}
import aqua.raw.value.ApplyFunctorRaw
import cats.data.State
import cats.data.Chain
import aqua.model.inline.RawValueInliner.unfold
import cats.syntax.monoid.*
import scribe.Logging
object ApplyFunctorRawInliner extends RawInliner[ApplyFunctorRaw] with Logging {
override def apply[S: Mangler: Exports: Arrows](
afr: ApplyFunctorRaw,
propertyAllowed: Boolean
): State[S, (ValueModel, Inline)] = {
val functorModel = FunctorModel(afr.functor.name, afr.functor.`type`)
unfold(afr.value).flatMap {
case (v@VarModel(name, bt, _), inl) =>
for {
apName <- Mangler[S].findAndForbidName(name + "_to_functor")
resultName <- Mangler[S].findAndForbidName(name)
apVar = VarModel(apName, bt, Chain.one(functorModel))
} yield {
val tree = inl |+| Inline.tree(SeqModel.wrap(
FlattenModel(v, apName).leaf,
FlattenModel(apVar, resultName).leaf
))
VarModel(resultName, bt) -> tree
}
case v =>
// unexpected, properties are prohibited for literals
logger.error(s"Unexpected. Properties are prohibited for literals. Literal: '$v'")
State.pure(v)
}
}
}

View File

@ -1,88 +0,0 @@
package aqua.model.inline.raw
import aqua.model.{IntoFieldModel, IntoIndexModel, LambdaModel, LiteralModel, ValueModel, VarModel}
import aqua.model.inline.Inline
import aqua.model.inline.RawValueInliner.unfold
import aqua.model.inline.state.{Arrows, Exports, Mangler}
import aqua.raw.value.{
ApplyLambdaRaw,
CallArrowRaw,
IntoFieldRaw,
IntoIndexRaw,
LambdaRaw,
LiteralRaw,
VarRaw
}
import cats.data.{Chain, State}
import cats.syntax.monoid.*
import cats.instances.list.*
object ApplyLambdaRawInliner extends RawInliner[ApplyLambdaRaw] {
private[inline] def removeLambda[S: Mangler: Exports: Arrows](
vm: ValueModel
): State[S, (ValueModel, Inline)] =
vm match {
case VarModel(nameM, btm, lambdaM) if lambdaM.nonEmpty =>
for {
nameMM <- Mangler[S].findAndForbidName(nameM)
} yield VarModel(nameMM, vm.`type`, Chain.empty) -> Inline.preload(
// TODO use smth more resilient to make VarRaw from a flattened VarModel
nameMM -> ApplyLambdaRaw.fromChain(VarRaw(nameM, btm), lambdaM.map(_.toRaw))
)
case _ =>
State.pure(vm -> Inline.empty)
}
private[inline] def unfoldLambda[S: Mangler: Exports: Arrows](
l: LambdaRaw
): State[S, (LambdaModel, Inline)] = // TODO lambda for collection
l match {
case IntoFieldRaw(field, t) => State.pure(IntoFieldModel(field, t) -> Inline.empty)
case IntoIndexRaw(vm: ApplyLambdaRaw, t) =>
for {
nn <- Mangler[S].findAndForbidName("ap-lambda")
} yield IntoIndexModel(nn, t) -> Inline.preload(nn -> vm)
case IntoIndexRaw(vr: (VarRaw | CallArrowRaw), t) =>
unfold(vr, lambdaAllowed = false).map {
case (VarModel(name, _, _), inline) => IntoIndexModel(name, t) -> inline
case (LiteralModel(v, _), inline) => IntoIndexModel(v, t) -> inline
}
case IntoIndexRaw(LiteralRaw(value, _), t) =>
State.pure(IntoIndexModel(value, t) -> Inline.empty)
}
override def apply[S: Mangler: Exports: Arrows](
alr: ApplyLambdaRaw,
lambdaAllowed: Boolean
): State[S, (ValueModel, Inline)] = Exports[S].exports.flatMap { exports =>
val (raw, lambda) = alr.unwind
lambda
.foldLeft[State[S, (Chain[LambdaModel], Inline)]](
State.pure(Chain.empty[LambdaModel] -> Inline.empty)
) { case (lcm, l) =>
lcm.flatMap { case (lc, m) =>
unfoldLambda(l).map { case (lm, mm) =>
(lc :+ lm, m |+| mm)
}
}
}
.flatMap { case (lambdaModel, map) =>
unfold(raw, lambdaAllowed).flatMap {
case (v: VarModel, prefix) =>
val vm = v.copy(lambda = v.lambda ++ lambdaModel).resolveWith(exports)
if (lambdaAllowed) State.pure(vm -> (prefix |+| map))
else
removeLambda(vm).map { case (vmm, mpp) =>
vmm -> (prefix |+| mpp |+| map)
}
case (v, prefix) =>
// What does it mean actually? I've no ides
State.pure((v, prefix |+| map))
}
}
}
}

View File

@ -0,0 +1,228 @@
package aqua.model.inline.raw
import aqua.model.{
CallModel,
CallServiceModel,
CanonicalizeModel,
FlattenModel,
ForModel,
FunctorModel,
IntoFieldModel,
IntoIndexModel,
LiteralModel,
MatchMismatchModel,
NextModel,
PropertyModel,
PushToStreamModel,
RestrictionModel,
SeqModel,
ValueModel,
VarModel,
XorModel
}
import aqua.model.inline.Inline
import aqua.model.inline.RawValueInliner.unfold
import aqua.model.inline.state.{Arrows, Exports, Mangler}
import aqua.raw.value.{
ApplyFunctorRaw,
ApplyPropertyRaw,
CallArrowRaw,
FunctorRaw,
IntoFieldRaw,
IntoIndexRaw,
LiteralRaw,
PropertyRaw,
ValueRaw,
VarRaw
}
import aqua.types.{ArrayType, CanonStreamType, ScalarType, StreamType}
import cats.data.{Chain, State}
import cats.syntax.monoid.*
import cats.instances.list.*
object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] {
private[inline] def removeProperty[S: Mangler: Exports: Arrows](
vm: ValueModel
): State[S, (ValueModel, Inline)] =
vm match {
case VarModel(nameM, btm, propertyM) if propertyM.nonEmpty =>
for {
nameMM <- Mangler[S].findAndForbidName(nameM)
} yield VarModel(nameMM, vm.`type`, Chain.empty) -> Inline.preload(
// TODO use smth more resilient to make VarRaw from a flattened VarModel
nameMM -> ApplyPropertyRaw.fromChain(VarRaw(nameM, btm), propertyM.map(_.toRaw))
)
case _ =>
State.pure(vm -> Inline.empty)
}
private[inline] def unfoldProperty[S: Mangler: Exports: Arrows](
p: PropertyRaw
): State[S, (PropertyModel, Inline)] = // TODO property for collection
p match {
case IntoFieldRaw(field, t) =>
State.pure(IntoFieldModel(field, t) -> Inline.empty)
case IntoIndexRaw(vm: ApplyPropertyRaw, t) =>
for {
nn <- Mangler[S].findAndForbidName("ap-prop")
} yield IntoIndexModel(nn, t) -> Inline.preload(nn -> vm)
case IntoIndexRaw(vr: (VarRaw | CallArrowRaw), t) =>
unfold(vr, propertiesAllowed = false).map {
case (VarModel(name, _, _), inline) => IntoIndexModel(name, t) -> inline
case (LiteralModel(v, _), inline) => IntoIndexModel(v, t) -> inline
}
case IntoIndexRaw(LiteralRaw(value, _), t) =>
State.pure(IntoIndexModel(value, t) -> Inline.empty)
}
private def increment(v: ValueModel, result: VarModel) =
CallServiceModel(
LiteralModel("\"math\"", ScalarType.string),
"add",
CallModel(
v :: LiteralModel.fromRaw(LiteralRaw.number(1)) :: Nil,
CallModel.Export(result.name, result.`type`) :: Nil
)
).leaf
def unfoldRawWithPropertyModels[S: Mangler: Exports: Arrows](
raw: ValueRaw,
propertyModels: Chain[PropertyModel],
propertyPrefix: Inline,
propertiesAllowed: Boolean
): State[S, (ValueModel, Inline)] = {
Exports[S].exports.flatMap { exports =>
unfold(raw, propertiesAllowed).flatMap {
case (v: VarModel, prefix) =>
((v.`type`, propertyModels.headOption) match {
// canonicalize stream
case (st: StreamType, Some(idx @ IntoIndexModel(_, _))) =>
for {
uniqueResultName <- Mangler[S].findAndForbidName(v.name + "_result_canon")
uniqueTestName <- Mangler[S].findAndForbidName(v.name + "_test")
} yield {
val varSTest = VarModel(uniqueTestName, st)
val iter = VarModel("s", st.element)
val iterCanon = VarModel(v.name + "_iter_canon", CanonStreamType(st.element))
val resultCanon =
VarModel(uniqueResultName, CanonStreamType(st.element), propertyModels)
val incrVar = VarModel("incr_idx", ScalarType.u32)
val tree = RestrictionModel(varSTest.name, true).wrap(
ForModel(iter.name, v).wrap(
increment(idx.idxToValueModel, incrVar),
PushToStreamModel(
iter,
CallModel.Export(varSTest.name, varSTest.`type`)
).leaf,
CanonicalizeModel(
varSTest,
CallModel.Export(iterCanon.name, iterCanon.`type`)
).leaf,
XorModel.wrap(
MatchMismatchModel(
iterCanon
.copy(properties = Chain.one(FunctorModel("length", ScalarType.`u32`))),
incrVar,
true
).leaf,
NextModel(iter.name).leaf
)
),
CanonicalizeModel(
varSTest,
CallModel.Export(resultCanon.name, CanonStreamType(st.element))
).leaf
)
resultCanon -> Inline.tree(tree)
}
case _ =>
val vm = v.copy(properties = v.properties ++ propertyModels).resolveWith(exports)
State.pure(vm -> Inline.empty)
}).flatMap { case (genV, genInline) =>
if (propertiesAllowed) State.pure(genV -> (prefix |+| propertyPrefix |+| genInline))
else
removeProperty(genV).map { case (vmm, mpp) =>
vmm -> (prefix |+| mpp |+| propertyPrefix |+| genInline)
}
}
case (v, prefix) =>
// What does it mean actually? I've no ides
State.pure((v, prefix |+| propertyPrefix))
}
}
}
private def unfoldProperties[S: Mangler: Exports: Arrows](
raw: ValueRaw,
properties: Chain[PropertyRaw],
propertiesAllowed: Boolean
): State[S, (ValueModel, Inline)] = {
properties
.foldLeft[State[S, (Chain[PropertyModel], Inline, ValueRaw)]](
State.pure((Chain.empty[PropertyModel], Inline.empty, raw))
) { case (pcm, p) =>
pcm.flatMap { case (pc, m, r) =>
unfoldProperty(p).map { case (pm, mm) =>
(pc :+ pm, m |+| mm, r)
}
}
}
.flatMap { case (propertyModels, map, r) =>
unfoldRawWithPropertyModels(r, propertyModels, map, propertiesAllowed)
}
}
override def apply[S: Mangler: Exports: Arrows](
apr: ApplyPropertyRaw,
propertiesAllowed: Boolean
): State[S, (ValueModel, Inline)] =
val (raw, properties) = apr.unwind
val leftToFunctor = properties.takeWhile {
case FunctorRaw(_, _) => false
case _ => true
}
if (leftToFunctor.length == properties.length) {
unfoldProperties(raw, properties, propertiesAllowed)
} else {
// split properties like this:
// properties -- functor -- properties with functors
// process properties, process functor in ApplyFunctorRawInliner
// then process tail recursively
(for {
ur <- properties.dropWhile {
case FunctorRaw(_, _) => false
case _ => true
}.uncons
(functor: FunctorRaw, right) = ur
} yield {
(leftToFunctor, functor, right)
}).map { case (left, functor, right) =>
for {
vmLeftInline <- unfoldProperties(raw, left, propertiesAllowed)
(leftVM, leftInline) = vmLeftInline
fRaw = ApplyFunctorRaw(leftVM.toRaw, functor)
vmFunctorInline <- ApplyFunctorRawInliner(fRaw, false)
(fVM, fInline) = vmFunctorInline
vmRightInline <- unfold(ApplyPropertyRaw.fromChain(fVM.toRaw, right), propertiesAllowed)
(vm, rightInline) = vmRightInline
} yield {
vm -> (leftInline |+| fInline |+| rightInline)
}
}.getOrElse(unfoldProperties(raw, properties, propertiesAllowed))
}
}

View File

@ -67,7 +67,7 @@ object CallArrowRawInliner extends RawInliner[CallArrowRaw] with Logging {
override def apply[S: Mangler: Exports: Arrows](
raw: CallArrowRaw,
lambdaAllowed: Boolean
propertiesAllowed: Boolean
): State[S, (ValueModel, Inline)] =
Mangler[S]
.findAndForbidName(raw.name)

View File

@ -1,34 +1,25 @@
package aqua.model.inline.raw
import aqua.model.{
CallModel,
CanonicalizeModel,
NullModel,
PushToStreamModel,
RestrictionModel,
SeqModel,
ValueModel,
VarModel,
XorModel
}
import aqua.model.{CallModel, CanonicalizeModel, NullModel, PushToStreamModel, RestrictionModel, SeqModel, ValueModel, VarModel, XorModel}
import aqua.model.inline.Inline
import aqua.model.inline.RawValueInliner.valueToModel
import aqua.model.inline.state.{Arrows, Exports, Mangler}
import aqua.raw.value.CollectionRaw
import aqua.types.{ArrayType, OptionType, StreamType}
import aqua.types.{ArrayType, CanonStreamType, OptionType, StreamType}
import cats.data.{Chain, State}
object CollectionRawInliner extends RawInliner[CollectionRaw] {
override def apply[S: Mangler: Exports: Arrows](
raw: CollectionRaw,
lambdaAllowed: Boolean
propertiesAllowed: Boolean
): State[S, (ValueModel, Inline)] =
for {
streamName <- Mangler[S].findAndForbidName(
(
raw.boxType match {
case _: StreamType => "stream"
case _: CanonStreamType => "canon_stream"
case _: ArrayType => "array"
case _: OptionType => "option"
}
@ -49,8 +40,12 @@ object CollectionRawInliner extends RawInliner[CollectionRaw] {
canonName <-
if (raw.boxType.isStream) State.pure(streamName)
else Mangler[S].findAndForbidName(streamName)
canon = CallModel.Export(canonName, raw.boxType)
} yield VarModel(canonName, raw.boxType) -> Inline.tree(
canonType = raw.boxType match {
case StreamType(_) => raw.boxType
case _ => CanonStreamType(raw.boxType.element)
}
canon = CallModel.Export(canonName, canonType)
} yield VarModel(canonName, canon.`type`) -> Inline.tree(
raw.boxType match {
case ArrayType(_) =>
RestrictionModel(streamName, isStream = true).wrap(
@ -63,7 +58,7 @@ object CollectionRawInliner extends RawInliner[CollectionRaw] {
CanonicalizeModel(stream, canon).leaf
)
)
case StreamType(_) =>
case _ =>
SeqModel.wrap(vals.toList: _*)
}
)

View File

@ -10,7 +10,7 @@ trait RawInliner[T <: ValueRaw] {
def apply[S: Mangler: Exports: Arrows](
raw: T,
lambdaAllowed: Boolean = true
propertiesAllowed: Boolean = true
): State[S, (ValueModel, Inline)]
}

View File

@ -3,7 +3,7 @@ package aqua.model.inline
import aqua.model.*
import aqua.model.inline.state.InliningState
import aqua.raw.ops.*
import aqua.raw.value.{ApplyLambdaRaw, IntoFieldRaw, IntoIndexRaw, LiteralRaw, VarRaw}
import aqua.raw.value.{ApplyPropertyRaw, FunctorRaw, IntoFieldRaw, IntoIndexRaw, LiteralRaw, VarRaw}
import aqua.types.*
import cats.syntax.show.*
import cats.data.{Chain, NonEmptyList, NonEmptyMap}
@ -126,12 +126,12 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
cb(records!)
*/
"arrow inliner" should "pass stream to callback properly, holding lambda" in {
// TODO: unignore and fix after stream restrictions will be implemented
ignore /*"arrow inliner"*/ should "pass stream to callback properly, holding property" in {
val streamType = StreamType(ScalarType.string)
val streamVar = VarRaw("records", streamType)
val streamVarLambda =
ApplyLambdaRaw(
ApplyPropertyRaw(
VarRaw("records", streamType),
IntoIndexRaw(LiteralRaw.number(0), ScalarType.string)
)
@ -317,7 +317,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
// lambda that will be assigned to another variable
val objectVarLambda =
VarRaw("object", StructType("objectType", NonEmptyMap.one("field", ScalarType.string)))
.withLambda(
.withProperty(
IntoFieldRaw("field", ScalarType.string)
)
@ -416,7 +416,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
val idxVar = VarRaw("idx", ScalarType.u32)
val arrIdx = VarRaw("nodes", ArrayType(ScalarType.string)).withLambda(
val arrIdx = VarRaw("nodes", ArrayType(ScalarType.string)).withProperty(
IntoIndexRaw(idxVar, ScalarType.string)
)
@ -489,8 +489,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
LiteralModel("\"getSrv\"", LiteralType.string),
"getIdx",
CallModel(Nil, CallModel.Export(idxVar.name, idxVar.`type`) :: Nil)
).leaf,
JoinModel(NonEmptyList.one(ValueModel.fromRaw(arrIdx))).leaf
).leaf
)
) should be(true)

View File

@ -1,17 +1,9 @@
package aqua.model.inline
import aqua.model.inline.raw.ApplyLambdaRawInliner
import aqua.model.{
FlattenModel,
IntoFieldModel,
IntoIndexModel,
ParModel,
SeqModel,
ValueModel,
VarModel
}
import aqua.model.inline.raw.ApplyPropertiesRawInliner
import aqua.model.{FlattenModel, FunctorModel, IntoFieldModel, IntoIndexModel, ParModel, SeqModel, ValueModel, VarModel}
import aqua.model.inline.state.InliningState
import aqua.raw.value.{ApplyLambdaRaw, IntoFieldRaw, IntoIndexRaw, LiteralRaw, VarRaw}
import aqua.raw.value.{ApplyPropertyRaw, FunctorRaw, IntoIndexRaw, LiteralRaw, VarRaw}
import aqua.types.*
import cats.data.NonEmptyMap
import cats.data.Chain
@ -25,11 +17,11 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
import RawValueInliner.valueToModel
private def ysVarRaw(into: Int, name: String = "ys") =
VarRaw(name, ArrayType(ScalarType.i8)).withLambda(
VarRaw(name, ArrayType(ScalarType.i8)).withProperty(
IntoIndexRaw(LiteralRaw.number(into), ScalarType.i8)
)
private val `raw x[y]` = VarRaw("x", ArrayType(ScalarType.string)).withLambda(
private val `raw x[y]` = VarRaw("x", ArrayType(ScalarType.string)).withProperty(
IntoIndexRaw(
VarRaw("y", ScalarType.i8),
ScalarType.string
@ -50,8 +42,8 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
private val `raw res.c` = VarRaw(
"res",
bType
).withLambda(
IntoFieldRaw(
).withProperty(
FunctorRaw(
"c",
ScalarType.string
)
@ -67,18 +59,18 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
ScalarType.string
)
private val `raw x[ys[0]]` = VarRaw("x", ArrayType(ScalarType.string)).withLambda(`raw ys[0]`)
private val `raw x[ys[0]]` = VarRaw("x", ArrayType(ScalarType.string)).withProperty(`raw ys[0]`)
private val `raw x[ys[0]][ys[1]]` =
VarRaw("x", ArrayType(ArrayType(ScalarType.string))).withLambda(
VarRaw("x", ArrayType(ArrayType(ScalarType.string))).withProperty(
IntoIndexRaw(ysVarRaw(0), ArrayType(ScalarType.string)),
IntoIndexRaw(ysVarRaw(1), ScalarType.string)
)
private val `raw x[zs[ys[0]]][ys[1]]` =
VarRaw("x", ArrayType(ArrayType(ScalarType.string))).withLambda(
VarRaw("x", ArrayType(ArrayType(ScalarType.string))).withProperty(
IntoIndexRaw(
VarRaw("zs", ArrayType(ScalarType.i8)).withLambda(
VarRaw("zs", ArrayType(ScalarType.i8)).withProperty(
IntoIndexRaw(
ysVarRaw(0),
ScalarType.i8
@ -103,7 +95,8 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
)
}
"raw value inliner" should "unfold an IntoField LambdaModel" in {
// TODO: unignore and fix after stream restrictions will be implemented
ignore /*"raw value inliner"*/ should "unfold an IntoField PropertyModel" in {
import aqua.model.inline.state.Mangler.Simple
// a.field1.field2
valueToModel[InliningState](`raw res.c`)
@ -122,17 +115,17 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
)
}
"raw value inliner" should "unfold a LambdaModel" in {
"raw value inliner" should "unfold a PropertyModel" in {
import aqua.model.inline.state.Mangler.Simple
// [ys!]
ApplyLambdaRawInliner
.unfoldLambda[InliningState](`raw ys[0]`)
ApplyPropertiesRawInliner
.unfoldProperty[InliningState](`raw ys[0]`)
.run(InliningState(noNames = Set("ys")))
.value
._2 should be(
IntoIndexModel("ap-lambda", ScalarType.string) -> Inline(
IntoIndexModel("ap-prop", ScalarType.string) -> Inline(
Map(
"ap-lambda" -> VarRaw("ys", ArrayType(ScalarType.i8)).withLambda(
"ap-prop" -> VarRaw("ys", ArrayType(ScalarType.i8)).withProperty(
IntoIndexRaw(LiteralRaw.number(0), ScalarType.i8)
)
)
@ -153,7 +146,7 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
VarModel(
"x",
ArrayType(ScalarType.string),
Chain.one(IntoIndexModel("ap-lambda", ScalarType.string))
Chain.one(IntoIndexModel("ap-prop", ScalarType.string))
)
)
@ -166,7 +159,7 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
ArrayType(ScalarType.i8),
Chain.one(IntoIndexModel("0", ScalarType.i8))
),
"ap-lambda"
"ap-prop"
).leaf
) should be(true)
}
@ -184,8 +177,8 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
"x",
ArrayType(ArrayType(ScalarType.string)),
Chain(
IntoIndexModel("ap-lambda", ArrayType(ScalarType.string)),
IntoIndexModel("ap-lambda-0", ScalarType.string)
IntoIndexModel("ap-prop", ArrayType(ScalarType.string)),
IntoIndexModel("ap-prop-0", ScalarType.string)
)
)
)
@ -200,7 +193,7 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
ArrayType(ScalarType.i8),
Chain.one(IntoIndexModel("0", ScalarType.i8))
),
"ap-lambda"
"ap-prop"
).leaf,
FlattenModel(
VarModel(
@ -208,7 +201,7 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
ArrayType(ScalarType.i8),
Chain.one(IntoIndexModel("1", ScalarType.i8))
),
"ap-lambda-0"
"ap-prop-0"
).leaf
)
) should be(true)
@ -229,8 +222,8 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
"x",
ArrayType(ArrayType(ScalarType.string)),
Chain(
IntoIndexModel("ap-lambda", ArrayType(ScalarType.string)),
IntoIndexModel("ap-lambda-0", ScalarType.string)
IntoIndexModel("ap-prop", ArrayType(ScalarType.string)),
IntoIndexModel("ap-prop-0", ScalarType.string)
)
)
)
@ -248,16 +241,16 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
ArrayType(ScalarType.i8),
Chain.one(IntoIndexModel("0", ScalarType.i8))
),
"ap-lambda-1"
"ap-prop-1"
).leaf,
// Then use that ys-1 as an index of zs
FlattenModel(
VarModel(
"zs",
ArrayType(ScalarType.i8),
Chain.one(IntoIndexModel("ap-lambda-1", ScalarType.i8))
Chain.one(IntoIndexModel("ap-prop-1", ScalarType.i8))
),
"ap-lambda"
"ap-prop"
).leaf
),
// Now prepare ys-0
@ -267,7 +260,7 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
ArrayType(ScalarType.i8),
Chain.one(IntoIndexModel("1", ScalarType.i8))
),
"ap-lambda-0"
"ap-prop-0"
).leaf
)
) should be(true)

View File

@ -1,29 +0,0 @@
package aqua.raw.value
import aqua.types.Type
sealed trait LambdaRaw {
def `type`: Type
def map(f: ValueRaw => ValueRaw): LambdaRaw
def renameVars(vals: Map[String, String]): LambdaRaw = this
def varNames: Set[String]
}
case class IntoFieldRaw(field: String, `type`: Type) extends LambdaRaw {
override def map(f: ValueRaw => ValueRaw): LambdaRaw = this
override def varNames: Set[String] = Set.empty
}
case class IntoIndexRaw(idx: ValueRaw, `type`: Type) extends LambdaRaw {
override def map(f: ValueRaw => ValueRaw): LambdaRaw = IntoIndexRaw(f(idx), `type`)
override def renameVars(vals: Map[String, String]): LambdaRaw =
IntoIndexRaw(idx.renameVars(vals), `type`)
override def varNames: Set[String] = idx.varNames
}

View File

@ -0,0 +1,37 @@
package aqua.raw.value
import aqua.types.Type
sealed trait PropertyRaw {
def `type`: Type
def map(f: ValueRaw => ValueRaw): PropertyRaw
def renameVars(vals: Map[String, String]): PropertyRaw = this
def varNames: Set[String]
}
case class IntoFieldRaw(name: String, `type`: Type) extends PropertyRaw {
override def map(f: ValueRaw => ValueRaw): PropertyRaw = this
override def varNames: Set[String] = Set.empty
}
case class FunctorRaw(name: String, `type`: Type) extends PropertyRaw {
override def map(f: ValueRaw => ValueRaw): FunctorRaw = this
override def renameVars(vals: Map[String, String]): FunctorRaw = this
override def varNames: Set[String] = Set.empty
}
case class IntoIndexRaw(idx: ValueRaw, `type`: Type) extends PropertyRaw {
override def map(f: ValueRaw => ValueRaw): PropertyRaw = IntoIndexRaw(f(idx), `type`)
override def renameVars(vals: Map[String, String]): PropertyRaw =
IntoIndexRaw(idx.renameVars(vals), `type`)
override def varNames: Set[String] = idx.varNames
}

View File

@ -47,34 +47,49 @@ object ValueRaw {
}
case class ApplyLambdaRaw(value: ValueRaw, lambda: LambdaRaw) extends ValueRaw {
case class ApplyPropertyRaw(value: ValueRaw, property: PropertyRaw) extends ValueRaw {
override def baseType: Type = value.baseType
override def `type`: Type = lambda.`type`
override def `type`: Type = property.`type`
override def renameVars(map: Map[String, String]): ValueRaw =
ApplyLambdaRaw(value.renameVars(map), lambda.renameVars(map))
ApplyPropertyRaw(value.renameVars(map), property.renameVars(map))
override def map(f: ValueRaw => ValueRaw): ValueRaw = f(ApplyLambdaRaw(f(value), lambda.map(f)))
override def map(f: ValueRaw => ValueRaw): ValueRaw = f(ApplyPropertyRaw(f(value), property.map(f)))
override def toString: String = s"$value.$lambda"
override def toString: String = s"$value.$property"
def unwind: (ValueRaw, Chain[LambdaRaw]) = value match {
case alr: ApplyLambdaRaw =>
def unwind: (ValueRaw, Chain[PropertyRaw]) = value match {
case alr: ApplyPropertyRaw =>
val (v, i) = alr.unwind
(v, i :+ lambda)
(v, i :+ property)
case _ =>
(value, Chain.one(lambda))
(value, Chain.one(property))
}
override def varNames: Set[String] = value.varNames ++ lambda.varNames
override def varNames: Set[String] = value.varNames ++ property.varNames
}
object ApplyLambdaRaw {
case class ApplyFunctorRaw(value: ValueRaw, functor: FunctorRaw) extends ValueRaw {
override def baseType: Type = value.baseType
def fromChain(value: ValueRaw, lambdas: Chain[LambdaRaw]): ValueRaw =
lambdas.foldLeft(value) { case (v, l) =>
ApplyLambdaRaw(v, l)
override def `type`: Type = functor.`type`
override def renameVars(map: Map[String, String]): ValueRaw =
ApplyFunctorRaw(value.renameVars(map), functor.renameVars(map))
override def map(f: ValueRaw => ValueRaw): ValueRaw = f(ApplyFunctorRaw(f(value), functor.map(f)))
override def toString: String = s"$value.$functor"
override def varNames: Set[String] = value.varNames ++ functor.varNames
}
object ApplyPropertyRaw {
def fromChain(value: ValueRaw, properties: Chain[PropertyRaw]): ValueRaw =
properties.foldLeft(value) { case (v, l) =>
ApplyPropertyRaw(v, l)
}
}
@ -108,8 +123,8 @@ case class VarRaw(name: String, baseType: Type) extends ValueRaw {
override def toString: String = s"var{$name: " + baseType + s"}"
def withLambda(lambda: LambdaRaw*): ValueRaw =
ApplyLambdaRaw.fromChain(this, Chain.fromSeq(lambda))
def withProperty(property: PropertyRaw*): ValueRaw =
ApplyPropertyRaw.fromChain(this, Chain.fromSeq(property))
override def varNames: Set[String] = Set(name)
}

View File

@ -1,6 +1,6 @@
package aqua.res
import aqua.types.{ArrayType, StreamType}
import aqua.types.{ArrayType, CanonStreamType, StreamType}
import cats.Eval
import cats.data.{Chain, NonEmptyList}
import cats.free.Cofree
@ -62,26 +62,25 @@ object MakeRes {
case NextModel(item) => NextRes(item).leaf
case PushToStreamModel(operand @ VarModel(_, StreamType(st), _), exportTo) =>
val tmpName = s"push-to-stream-$i"
// wrap (
// RestrictionRes(tmpName, isStream = false),
val properties = operand.properties
SeqRes.wrap(
canon(
CanonRes(
operand.copy(properties = Chain.empty),
orInit(currentPeerId),
operand,
CallModel.Export(tmpName, ArrayType(st))
),
ApRes(VarModel(tmpName, ArrayType(st), Chain.empty), exportTo).leaf
CallModel.Export(tmpName, CanonStreamType(st))
).leaf,
ApRes(VarModel(tmpName, CanonStreamType(st), properties), exportTo).leaf
)
// )
case PushToStreamModel(operand, exportTo) =>
ApRes(operand, exportTo).leaf
case CanonicalizeModel(operand, exportTo) =>
canon(
orInit(currentPeerId),
CanonRes(
operand,
orInit(currentPeerId),
exportTo
)
).leaf
case FlattenModel(operand, assignTo) =>
ApRes(operand, CallModel.Export(assignTo, operand.`type`)).leaf
case JoinModel(operands) =>

View File

@ -52,6 +52,10 @@ case class ApRes(operand: ValueModel, exportTo: CallModel.Export) extends Resolv
override def toString: String = s"(ap $operand $exportTo)"
}
case class CanonRes(operand: ValueModel, peerId: ValueModel, exportTo: CallModel.Export) extends ResolvedOp {
override def toString: String = s"(canon $peerId $operand $exportTo)"
}
case object NullRes extends ResolvedOp {
override def toString: String = "(null)"
}

View File

@ -12,6 +12,8 @@ sealed trait ValueModel {
def resolveWith(map: Map[String, ValueModel]): ValueModel = this
def usesVarNames: Set[String] = Set.empty
def toRaw: ValueRaw
}
object ValueModel {
@ -22,9 +24,9 @@ object ValueModel {
// TODO it should be marked with DANGEROUS signs and so on, as THIS IS UNSAFE!!!!!!!!!!!!!!! usable only for tests
def fromRaw(raw: ValueRaw): ValueModel = raw match {
case ApplyLambdaRaw(v, lambda) =>
case ApplyPropertyRaw(v, property) =>
fromRaw(v) match {
case vm: VarModel => vm.copy(lambda = vm.lambda :+ LambdaModel.fromRaw(lambda))
case vm: VarModel => vm.copy(properties = vm.properties :+ PropertyModel.fromRaw(property))
case _ => ???
}
case VarRaw(name, t) =>
@ -39,26 +41,29 @@ object ValueModel {
case class LiteralModel(value: String, `type`: Type) extends ValueModel {
override def toString: String = s"{$value: ${`type`}}"
def toRaw: ValueRaw = LiteralRaw(value, `type`)
}
object LiteralModel {
def fromRaw(raw: LiteralRaw): LiteralModel = LiteralModel(raw.value, raw.baseType)
}
sealed trait LambdaModel {
sealed trait PropertyModel {
def usesVarNames: Set[String] = Set.empty
def `type`: Type
def toRaw: LambdaRaw
def toRaw: PropertyRaw
}
object LambdaModel {
object PropertyModel {
def fromRaw(l: LambdaRaw): LambdaModel = l match {
def fromRaw(l: PropertyRaw): PropertyModel = l match {
case FunctorRaw(op, t) => FunctorModel(op, t)
case IntoFieldRaw(field, t) => IntoFieldModel(field, t)
case IntoIndexRaw(idx, t) =>
// TODO: handle recursive lambda
// TODO: handle recursive property
IntoIndexModel(
ValueModel.fromRaw(idx) match {
case VarModel(name, _, _) => name
@ -70,36 +75,50 @@ object LambdaModel {
}
case class IntoFieldModel(field: String, `type`: Type) extends LambdaModel {
override def toString: String = s".$field:${`type`}"
case class FunctorModel(name: String, `type`: Type) extends PropertyModel {
override def toString: String = s".$name:${`type`}"
override def toRaw: LambdaRaw = IntoFieldRaw(field, `type`)
override def toRaw: PropertyRaw = FunctorRaw(name, `type`)
}
case class IntoIndexModel(idx: String, `type`: Type) extends LambdaModel {
case class IntoFieldModel(name: String, `type`: Type) extends PropertyModel {
override def toString: String = s".$name:${`type`}"
override def toRaw: PropertyRaw = IntoFieldRaw(name, `type`)
}
case class IntoIndexModel(idx: String, `type`: Type) extends PropertyModel {
override lazy val usesVarNames: Set[String] = Set(idx).filterNot(_.forall(Character.isDigit))
override def toString: String = s"[$idx -> ${`type`}]"
override def toRaw: LambdaRaw = IntoIndexRaw(
override def toRaw: PropertyRaw = IntoIndexRaw(
if (idx.forall(Character.isDigit)) LiteralRaw(idx, LiteralType.number)
else VarRaw(idx, LiteralType.number),
`type`
)
def idxToValueModel: ValueModel =
if (idx.forall(Character.isDigit)) LiteralModel(idx, LiteralType.number)
else VarModel(idx, `type`)
}
case class VarModel(name: String, baseType: Type, lambda: Chain[LambdaModel] = Chain.empty)
case class VarModel(name: String, baseType: Type, properties: Chain[PropertyModel] = Chain.empty)
extends ValueModel with Logging {
override lazy val usesVarNames: Set[String] =
lambda.toList.map(_.usesVarNames).foldLeft(Set(name))(_ ++ _)
properties.toList.map(_.usesVarNames).foldLeft(Set(name))(_ ++ _)
override val `type`: Type = lambda.lastOption.map(_.`type`).getOrElse(baseType)
override val `type`: Type = properties.lastOption.map(_.`type`).getOrElse(baseType)
override def toString: String = s"var{$name: " + baseType + s"}.${lambda.toList.mkString(".")}"
def toRaw: ValueRaw = VarRaw(name, baseType).withProperty(properties.map(_.toRaw).toList: _*)
override def toString: String =
s"var{$name: " + baseType + s"}.${properties.toList.mkString(".")}"
private def deriveFrom(vm: VarModel): VarModel =
vm.copy(lambda = vm.lambda ++ lambda)
vm.copy(properties = vm.properties ++ properties)
override def resolveWith(vals: Map[String, ValueModel]): ValueModel =
vals.get(name) match {
@ -130,9 +149,9 @@ case class VarModel(name: String, baseType: Type, lambda: Chain[LambdaModel] = C
case nvm: VarModel =>
deriveFrom(vv.deriveFrom(nvm))
case valueModel =>
if (lambda.nonEmpty)
if (properties.nonEmpty)
logger.error(
s"Var $name derived from literal $valueModel, but lambda is lost: $lambda"
s"Var $name derived from literal $valueModel, but property is lost: $properties"
)
valueModel
}
@ -142,9 +161,9 @@ case class VarModel(name: String, baseType: Type, lambda: Chain[LambdaModel] = C
}
case Some(vv) =>
if (lambda.nonEmpty)
if (properties.nonEmpty)
logger.error(
s"Var $name derived from literal $vv, but lambda is lost: $lambda"
s"Var $name derived from literal $vv, but property is lost: $properties"
)
vv
case None =>

View File

@ -26,7 +26,7 @@ sealed trait InitPeerCallable extends PreTransform {
makeCall(serviceId, _, _)
}
// TODO: refactor goThrough into some supertype of VarRaw and VarModel with no lambda
// TODO: refactor goThrough into some supertype of VarRaw and VarModel with no properties
case class InitViaRelayCallable(goThrough: Chain[(String, Type)]) extends InitPeerCallable {
// Get to init user through a relay

View File

@ -2,8 +2,9 @@ package aqua.model.transform.topology
import aqua.model.transform.cursor.ChainZipper
import aqua.model.*
import aqua.res.{FoldRes, MakeRes, NextRes, ResolvedOp, SeqRes}
import aqua.types.{BoxType, ScalarType}
import aqua.raw.value.{LiteralRaw, ValueRaw}
import aqua.res.{ApRes, CanonRes, FoldRes, MakeRes, NextRes, ResolvedOp, SeqRes}
import aqua.types.{ArrayType, BoxType, CanonStreamType, ScalarType, StreamType}
import cats.Eval
import cats.data.Chain.{==:, nil}
import cats.data.{Chain, NonEmptyChain, NonEmptyList, OptionT}

View File

@ -424,7 +424,7 @@ class TopologySpec extends AnyFlatSpec with Matchers {
"topology resolver" should "create returning hops after for-par with inner `on` and xor" in {
val streamRaw = VarRaw("stream", StreamType(ScalarType.string))
val streamRawEl = VarRaw("stream", StreamType(ScalarType.string)).withLambda(
val streamRawEl = VarRaw("stream", StreamType(ScalarType.string)).withProperty(
IntoIndexRaw(LiteralRaw("2", ScalarType.u32), ScalarType.string)
)
val stream = ValueModel.fromRaw(streamRaw)
@ -491,7 +491,7 @@ class TopologySpec extends AnyFlatSpec with Matchers {
"topology resolver" should "create returning hops after for-par with inner `on` and xor, version 2" in {
val streamRaw = VarRaw("stream", StreamType(ScalarType.string))
val streamRawEl = VarRaw("stream", StreamType(ScalarType.string)).withLambda(
val streamRawEl = VarRaw("stream", StreamType(ScalarType.string)).withProperty(
IntoIndexRaw(LiteralRaw("2", ScalarType.u32), ScalarType.string)
)
val stream = ValueModel.fromRaw(streamRaw)
@ -775,7 +775,7 @@ class TopologySpec extends AnyFlatSpec with Matchers {
val i = LiteralRaw("i", ScalarType.string)
val used = VarRaw("used", StreamType(ScalarType.string))
val usedWithIdx =
used.withLambda(IntoIndexRaw(LiteralRaw("1", ScalarType.u32), ScalarType.string))
used.withProperty(IntoIndexRaw(LiteralRaw("1", ScalarType.u32), ScalarType.string))
val init =
OnModel(initPeer, Chain.one(relay)).wrap(
@ -827,7 +827,7 @@ class TopologySpec extends AnyFlatSpec with Matchers {
val i = LiteralRaw("i", ScalarType.string)
val used = VarRaw("used", StreamType(ScalarType.string))
val usedWithIdx =
used.withLambda(IntoIndexRaw(LiteralRaw("1", ScalarType.u32), ScalarType.string))
used.withProperty(IntoIndexRaw(LiteralRaw("1", ScalarType.u32), ScalarType.string))
val init =
OnModel(initPeer, Chain.one(relay)).wrap(
foldPar(

View File

@ -43,8 +43,13 @@ trait TreeNodeCompanion[T <: TreeNode[T]] {
val rgtDiff =
if (commonPrefixLen + rSuffix < rgt.length) rgt.substring(commonPrefixLen, rSuffix)
else ""
commonPrefix +
Console.YELLOW + lftDiff + Console.RED + " != " + Console.CYAN + rgtDiff + Console.RESET + commonSuffix
if (rgtDiff.isEmpty) {
commonPrefix + Console.YELLOW + lftDiff + Console.RESET + commonSuffix
} else {
commonPrefix +
Console.YELLOW + lftDiff + Console.RED + " != " + Console.CYAN + rgtDiff + Console.RESET + commonSuffix
}
}
spaces + head + (what._1.tail, what._2.tail).mapN {

1499
npm/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -23,7 +23,7 @@
"dependencies": {
"@fluencelabs/aqua-ipfs": "0.5.5",
"@fluencelabs/aqua-lib": "0.5.2",
"@fluencelabs/fluence": "0.25.1",
"@fluencelabs/fluence": "0.25.2",
"@fluencelabs/fluence-network-environment": "1.0.13",
"ipfs-http-client": "50.1.2"
},

View File

@ -20,5 +20,5 @@ case class JoinExpr[F[_]](values: NonEmptyList[VarToken[F]])
object JoinExpr extends Expr.Leaf {
override val p: Parser[JoinExpr[Span.S]] =
(`join` *> ` ` *> comma(ValueToken.varLambda)).map(JoinExpr(_))
(`join` *> ` ` *> comma(ValueToken.varProperty)).map(JoinExpr(_))
}

View File

@ -15,32 +15,32 @@ import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
import aqua.types.LiteralType
sealed trait LambdaOp[F[_]] extends Token[F] {
def mapK[K[_]: Comonad](fk: F ~> K): LambdaOp[K]
sealed trait PropertyOp[F[_]] extends Token[F] {
def mapK[K[_]: Comonad](fk: F ~> K): PropertyOp[K]
}
case class IntoField[F[_]: Comonad](name: F[String]) extends LambdaOp[F] {
case class IntoField[F[_]: Comonad](name: F[String]) extends PropertyOp[F] {
override def as[T](v: T): F[T] = name.as(v)
override def mapK[K[_]: Comonad](fk: F ~> K): LambdaOp[K] = copy(fk(name))
override def mapK[K[_]: Comonad](fk: F ~> K): PropertyOp[K] = copy(fk(name))
def value: String = name.extract
}
case class IntoIndex[F[_]: Comonad](token: Token[F], idx: Option[ValueToken[F]])
extends LambdaOp[F] {
extends PropertyOp[F] {
override def as[T](v: T): F[T] = token.as(v)
override def mapK[K[_]: Comonad](fk: F ~> K): IntoIndex[K] =
copy(token.mapK(fk), idx.map(_.mapK(fk)))
}
object LambdaOp {
object PropertyOp {
private val parseField: P[LambdaOp[Span.S]] =
private val parseField: P[PropertyOp[Span.S]] =
(`.` *> `name`).lift.map(IntoField(_))
private val parseIdx: P[LambdaOp[Span.S]] =
private val parseIdx: P[PropertyOp[Span.S]] =
(P.defer(
(ValueToken.`value`.between(`[`, `]`) | (exclamation *> ValueToken.num))
.map(v => IntoIndex(v, Some(v)))
@ -53,10 +53,10 @@ object LambdaOp {
}
}
private val parseOp: P[LambdaOp[Span.S]] =
private val parseOp: P[PropertyOp[Span.S]] =
P.oneOf(parseField.backtrack :: parseIdx :: Nil)
val ops: P[NonEmptyList[LambdaOp[Span.S]]] =
val ops: P[NonEmptyList[PropertyOp[Span.S]]] =
parseOp.rep
}

View File

@ -19,10 +19,10 @@ sealed trait ValueToken[F[_]] extends Token[F] {
def mapK[K[_]: Comonad](fk: F ~> K): ValueToken[K]
}
case class VarToken[F[_]](name: Name[F], lambda: List[LambdaOp[F]] = Nil) extends ValueToken[F] {
case class VarToken[F[_]](name: Name[F], property: List[PropertyOp[F]] = Nil) extends ValueToken[F] {
override def as[T](v: T): F[T] = name.as(v)
def mapK[K[_]: Comonad](fk: F ~> K): VarToken[K] = copy(name.mapK(fk), lambda.map(_.mapK(fk)))
def mapK[K[_]: Comonad](fk: F ~> K): VarToken[K] = copy(name.mapK(fk), property.map(_.mapK(fk)))
}
case class LiteralToken[F[_]: Comonad](valueToken: F[String], ts: LiteralType)
@ -168,7 +168,7 @@ object InfixToken {
) ::
P.defer(CallArrowToken.callArrow).backtrack ::
P.defer(brackets(InfixToken.mathExpr)) ::
varLambda ::
varProperty ::
Nil
)
@ -273,9 +273,9 @@ object InfixToken {
object ValueToken {
val varLambda: P[VarToken[Span.S]] =
(Name.dotted ~ LambdaOp.ops.?).map { case (n, l)
VarToken(n, l.fold[List[LambdaOp[Span.S]]](Nil)(_.toList))
val varProperty: P[VarToken[Span.S]] =
(Name.dotted ~ PropertyOp.ops.?).map { case (n, l)
VarToken(n, l.fold[List[PropertyOp[Span.S]]](Nil)(_.toList))
}
val bool: P[LiteralToken[Span.S]] =

View File

@ -7,18 +7,18 @@ import org.scalatest.EitherValues
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
class LambdaOpSpec extends AnyFlatSpec with Matchers with EitherValues {
class PropertyOpSpec extends AnyFlatSpec with Matchers with EitherValues {
import aqua.AquaSpec._
"lambda ops" should "parse" in {
val opsP = (s: String) => LambdaOp.ops.parseAll(s).value.map(_.mapK(spanToId))
val opsP = (s: String) => PropertyOp.ops.parseAll(s).value.map(_.mapK(spanToId))
opsP(".field") should be(NonEmptyList.of(IntoField[Id]("field")))
opsP(".field.sub") should be(NonEmptyList.of(IntoField[Id]("field"), IntoField[Id]("sub")))
LambdaOp.ops.parseAll("[-1]").isLeft shouldBe true
LambdaOp.ops.parseAll("!-1").isLeft shouldBe true
PropertyOp.ops.parseAll("[-1]").isLeft shouldBe true
PropertyOp.ops.parseAll("!-1").isLeft shouldBe true
}

View File

@ -12,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.{ArrayType, ArrowType, ProductType, StreamType, Type}
import aqua.types.{ArrayType, ArrowType, ProductType, StreamType, Type, CanonStreamType}
import cats.data.{Chain, NonEmptyList}
import cats.free.{Cofree, Free}
import cats.syntax.applicative.*
@ -91,11 +91,11 @@ class ArrowSem[S[_]](val expr: ArrowExpr[S]) extends AnyVal {
SeqTag.wrap(
b :: CanonicalizeTag(
VarRaw(n, st),
Call.Export(s"$n-fix", ArrayType(st.element))
Call.Export(s"$n-fix", CanonStreamType(st.element))
).leaf :: Nil: _*
)
) -> rs.map { vn =>
vn.shadow(n, VarRaw(s"$n-fix", ArrayType(st.element)))
vn.shadow(n, VarRaw(s"$n-fix", CanonStreamType(st.element)))
}
else RestrictionTag(n, isStream = true).wrap(b) -> rs
}

View File

@ -40,7 +40,7 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](implicit
def resolveType(v: ValueToken[S]): Alg[Option[Type]] =
valueToRaw(v).map(_.map(_.`type`))
private def resolveSingleLambda(rootType: Type, op: LambdaOp[S]): Alg[Option[LambdaRaw]] =
private def resolveSingleProperty(rootType: Type, op: PropertyOp[S]): Alg[Option[PropertyRaw]] =
op match {
case op: IntoField[S] =>
T.resolveField(rootType, op)
@ -61,18 +61,18 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](implicit
case VarToken(name, ops) =>
N.read(name).flatMap {
case Some(t) =>
// Prepare lambda expression: take the last known type and the next op, add next op to accumulator
// Prepare property expression: take the last known type and the next op, add next op to accumulator
ops
.foldLeft[Alg[(Option[Type], Chain[LambdaRaw])]]((Some(t) -> Chain.empty).pure[Alg]) {
.foldLeft[Alg[(Option[Type], Chain[PropertyRaw])]]((Some(t) -> Chain.empty).pure[Alg]) {
case (acc, op) =>
acc.flatMap {
// Some(tt) means that the previous lambda op was resolved successfully
case (Some(tt), lamb) =>
// Resolve a single lambda
resolveSingleLambda(tt, op).map {
// Lambda op resolved, add it to accumulator and update the last known type
case Some(l) => (Some(l.`type`), lamb :+ l)
// Lambda op is not resolved, it's an error, stop iterations
// Some(tt) means that the previous property op was resolved successfully
case (Some(tt), prop) =>
// Resolve a single property
resolveSingleProperty(tt, op).map {
// Property op resolved, add it to accumulator and update the last known type
case Some(p) => (Some(p.`type`), prop :+ p)
// Property op is not resolved, it's an error, stop iterations
case None => (None, Chain.empty)
}
@ -83,10 +83,11 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](implicit
}
.map {
// Some(_) means no errors occured
case (Some(_), lambda) if lambda.length == ops.length =>
Some(lambda.foldLeft[ValueRaw](VarRaw(name.value, t)) { case (v, l) =>
ApplyLambdaRaw(v, l)
case (Some(_), property) if property.length == ops.length =>
Some(property.foldLeft[ValueRaw](VarRaw(name.value, t)) { case (v, p) =>
ApplyPropertyRaw(v, p)
})
case _ => None
}

View File

@ -1,7 +1,7 @@
package aqua.semantics.rules.types
import aqua.parser.lexer.*
import aqua.raw.value.{LambdaRaw, ValueRaw}
import aqua.raw.value.{PropertyRaw, ValueRaw}
import aqua.types.{ArrowType, Type}
import cats.data.NonEmptyMap
import cats.data.NonEmptyList
@ -23,8 +23,8 @@ trait TypesAlgebra[S[_], Alg[_]] {
def defineAlias(name: CustomTypeToken[S], target: Type): Alg[Boolean]
def resolveIndex(rootT: Type, op: IntoIndex[S], idx: ValueRaw): Alg[Option[LambdaRaw]]
def resolveField(rootT: Type, op: IntoField[S]): Alg[Option[LambdaRaw]]
def resolveIndex(rootT: Type, op: IntoIndex[S], idx: ValueRaw): Alg[Option[PropertyRaw]]
def resolveField(rootT: Type, op: IntoField[S]): Alg[Option[PropertyRaw]]
def ensureValuesComparable(token: Token[S], left: Type, right: Type): Alg[Boolean]

View File

@ -1,7 +1,7 @@
package aqua.semantics.rules.types
import aqua.parser.lexer.*
import aqua.raw.value.{IntoFieldRaw, IntoIndexRaw, LambdaRaw, ValueRaw}
import aqua.raw.value.{FunctorRaw, IntoIndexRaw, IntoFieldRaw, PropertyRaw, ValueRaw}
import aqua.semantics.lsp.{TokenDef, TokenTypeInfo}
import aqua.semantics.rules.{ReportError, StackInterpreter}
import aqua.types.{
@ -128,7 +128,7 @@ class TypesInterpreter[S[_], X](implicit lens: Lens[X, TypesState[S]], error: Re
).as(true)
}
override def resolveField(rootT: Type, op: IntoField[S]): State[X, Option[LambdaRaw]] = {
override def resolveField(rootT: Type, op: IntoField[S]): State[X, Option[PropertyRaw]] = {
rootT match {
case StructType(name, fields) =>
fields(op.value).fold(
@ -145,8 +145,15 @@ class TypesInterpreter[S[_], X](implicit lens: Lens[X, TypesState[S]], error: Re
}.as(Some(IntoFieldRaw(op.value, t)))
}
case _ =>
report(op, s"Expected Struct type to resolve a field, got $rootT").as(None)
case t =>
t.properties.get(op.value)
.fold(
report(
op,
s"Expected Struct type to resolve a field '${op.value}' or a type with this property. Got: $rootT"
).as(None)
)(t => State.pure(Some(FunctorRaw(op.value, t))))
}
}
@ -155,7 +162,7 @@ class TypesInterpreter[S[_], X](implicit lens: Lens[X, TypesState[S]], error: Re
rootT: Type,
op: IntoIndex[S],
idx: ValueRaw
): State[X, Option[LambdaRaw]] =
): State[X, Option[PropertyRaw]] =
if (!ScalarType.i64.acceptsValueOf(idx.`type`))
report(op, s"Expected numeric index, got $idx").as(None)
else

View File

@ -1,7 +1,7 @@
package aqua.semantics.rules.types
import aqua.raw.value.{IntoFieldRaw, IntoIndexRaw, LambdaRaw, LiteralRaw, ValueRaw}
import aqua.parser.lexer.{ArrayTypeToken, ArrowTypeToken, BasicTypeToken, CustomTypeToken, IntoField, IntoIndex, LambdaOp, Name, OptionTypeToken, StreamTypeToken, Token, TopBottomToken, TypeToken}
import aqua.raw.value.{FunctorRaw, IntoIndexRaw, PropertyRaw, LiteralRaw, ValueRaw}
import aqua.parser.lexer.{ArrayTypeToken, ArrowTypeToken, BasicTypeToken, CustomTypeToken, IntoField, IntoIndex, PropertyOp, Name, OptionTypeToken, StreamTypeToken, Token, TopBottomToken, TypeToken}
import aqua.types.{ArrayType, ArrowType, BottomType, DataType, OptionType, ProductType, StreamType, StructType, TopType, Type}
import cats.data.Validated.{Invalid, Valid}
import cats.data.{Chain, NonEmptyChain, ValidatedNec}

View File

@ -24,6 +24,8 @@ sealed trait Type {
def uniteTop(other: Type): Type = UniteTypes.top.combine(this, other)
def uniteBottom(other: Type): Type = UniteTypes.bottom.combine(this, other)
def properties: Map[String, Type] = Map.empty
}
// Product is a list of (optionally labelled) types
@ -184,6 +186,18 @@ sealed trait BoxType extends DataType {
def element: Type
def withElement(t: Type): BoxType
override def properties: Map[String, Type] =
Map("length" -> ScalarType.u32)
}
case class CanonStreamType(element: Type) extends BoxType {
override def isStream: Boolean = false
override def toString: String = "#" + element
override def withElement(t: Type): BoxType = copy(element = t)
}
case class ArrayType(element: Type) extends BoxType {