mirror of
https://github.com/fluencelabs/aqua.git
synced 2025-04-24 22:42:13 +00:00
Model/raw refactoring (#398)
* Introducing model/raw subproject * ValueRaw WIP * ValueRaw WIP * ValueModel.fromRaw WIP * Recursive variables renaming * Tests * recursiveRaw * Arrow Inliner refactoring * desugarize takes its place WIP * Sugar.desugarize maybe works * Some movings/renamings * Compile bug fixed * Updated scalafmt * Fix for service defaultId * Map values recursively
This commit is contained in:
parent
a051ab0efc
commit
061e896b63
@ -1,6 +1,7 @@
|
||||
version = 2.7.5
|
||||
version = 3.3.1
|
||||
runner.dialect = scala3
|
||||
|
||||
docstrings = JavaDoc
|
||||
docstrings.style = Asterisk
|
||||
|
||||
maxColumn = 100
|
||||
|
||||
@ -22,15 +23,21 @@ continuationIndent {
|
||||
extendSite = 4
|
||||
}
|
||||
|
||||
danglingParentheses = true
|
||||
danglingParentheses.preset = true
|
||||
|
||||
newlines {
|
||||
alwaysBeforeTopLevelStatements = true
|
||||
sometimesBeforeColonInMethodReturnType = true
|
||||
penalizeSingleSelectMultiArgList = false
|
||||
alwaysBeforeElseAfterCurlyIf = false
|
||||
neverInResultType = false
|
||||
}
|
||||
# newlines {
|
||||
# alwaysBeforeTopLevelStatements = true
|
||||
# sometimesBeforeColonInMethodReturnType = true
|
||||
# penalizeSingleSelectMultiArgList = false
|
||||
# alwaysBeforeElseAfterCurlyIf = false
|
||||
# neverInResultType = false
|
||||
# }
|
||||
|
||||
newlines.topLevelStatementBlankLines = [
|
||||
{
|
||||
blanks { before = 1 }
|
||||
}
|
||||
]
|
||||
|
||||
spaces {
|
||||
afterKeywordBeforeParen = true
|
||||
|
@ -1,8 +1,8 @@
|
||||
package aqua.backend.air
|
||||
|
||||
import aqua.model.*
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.transform.res.*
|
||||
import aqua.raw.ops.Call
|
||||
import aqua.types.StreamType
|
||||
import cats.Eval
|
||||
import cats.data.Chain
|
||||
@ -18,8 +18,6 @@ object AirGen extends Logging {
|
||||
|
||||
def lambdaToString(ls: List[LambdaModel]): String = ls match {
|
||||
case Nil => ""
|
||||
case IntoArrayModel(_) :: tail =>
|
||||
s"[@${lambdaToString(tail)}]"
|
||||
case IntoFieldModel(field, _) :: tail =>
|
||||
s".$field${lambdaToString(tail)}"
|
||||
case IntoIndexModel(idx, _) :: tail =>
|
||||
|
@ -5,6 +5,7 @@ import aqua.backend.ts.TypeScriptCommon.fnDef
|
||||
import aqua.model.transform.res.ServiceRes
|
||||
|
||||
case class TSServiceTypes(srv: ServiceRes) extends ServiceTypes {
|
||||
|
||||
import TypeScriptTypes.*
|
||||
|
||||
private val serviceTypeName = s"${srv.name}Def";
|
||||
|
20
build.sbt
20
build.sbt
@ -32,7 +32,7 @@ val commons = Seq(
|
||||
"-language:implicitConversions",
|
||||
"-unchecked",
|
||||
"-Ykind-projector"
|
||||
// "-Xfatal-warnings"
|
||||
// "-Xfatal-warnings"
|
||||
)
|
||||
}
|
||||
)
|
||||
@ -97,6 +97,18 @@ lazy val linker = crossProject(JVMPlatform, JSPlatform)
|
||||
.settings(commons: _*)
|
||||
.dependsOn(parser)
|
||||
|
||||
lazy val raw = crossProject(JVMPlatform, JSPlatform)
|
||||
.withoutSuffixFor(JVMPlatform)
|
||||
.crossType(CrossType.Pure)
|
||||
.in(file("model/raw"))
|
||||
.settings(commons: _*)
|
||||
.settings(
|
||||
libraryDependencies ++= Seq(
|
||||
"org.typelevel" %%% "cats-free" % catsV
|
||||
)
|
||||
)
|
||||
.dependsOn(types)
|
||||
|
||||
lazy val model = crossProject(JVMPlatform, JSPlatform)
|
||||
.withoutSuffixFor(JVMPlatform)
|
||||
.crossType(CrossType.Pure)
|
||||
@ -106,14 +118,14 @@ lazy val model = crossProject(JVMPlatform, JSPlatform)
|
||||
"org.typelevel" %%% "cats-free" % catsV
|
||||
)
|
||||
)
|
||||
.dependsOn(types)
|
||||
.dependsOn(types, raw)
|
||||
|
||||
lazy val transform = crossProject(JVMPlatform, JSPlatform)
|
||||
.withoutSuffixFor(JVMPlatform)
|
||||
.crossType(CrossType.Pure)
|
||||
.in(file("model/transform"))
|
||||
.settings(commons: _*)
|
||||
.dependsOn(model)
|
||||
.dependsOn(model, raw)
|
||||
|
||||
lazy val `test-kit` = crossProject(JVMPlatform, JSPlatform)
|
||||
.withoutSuffixFor(JVMPlatform)
|
||||
@ -132,7 +144,7 @@ lazy val semantics = crossProject(JVMPlatform, JSPlatform)
|
||||
"com.github.julien-truffaut" %%% "monocle-macro" % monocleV
|
||||
)
|
||||
)
|
||||
.dependsOn(model, `test-kit` % Test, parser)
|
||||
.dependsOn(raw, `test-kit` % Test, parser)
|
||||
|
||||
lazy val compiler = crossProject(JVMPlatform, JSPlatform)
|
||||
.withoutSuffixFor(JVMPlatform)
|
||||
|
@ -2,16 +2,17 @@ package aqua.builder
|
||||
|
||||
import aqua.backend.{ArgDefinition, PrimitiveType, ServiceDef, ServiceFunctionDef}
|
||||
import aqua.js.{CallJsFunction, CallServiceHandler, FluencePeer}
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.func.raw.CallServiceTag
|
||||
import aqua.model.{LiteralModel, VarModel}
|
||||
import aqua.raw.ops
|
||||
import aqua.raw.ops.{Call, CallServiceTag}
|
||||
import aqua.raw.value.LiteralRaw
|
||||
|
||||
import scalajs.js
|
||||
import scala.concurrent.Promise
|
||||
|
||||
// Service that can return argument to use it from a code
|
||||
case class ArgumentGetter(serviceId: String, value: VarModel, arg: scalajs.js.Dynamic)
|
||||
extends ServiceFunction {
|
||||
extends ServiceFunction {
|
||||
|
||||
def registerService(peer: FluencePeer): Unit = {
|
||||
CallJsFunction.registerService(
|
||||
@ -30,13 +31,12 @@ case class ArgumentGetter(serviceId: String, value: VarModel, arg: scalajs.js.Dy
|
||||
)
|
||||
}
|
||||
|
||||
def callTag(): CallServiceTag = {
|
||||
def callTag(): CallServiceTag =
|
||||
CallServiceTag(
|
||||
LiteralModel.quote(serviceId),
|
||||
LiteralRaw.quote(serviceId),
|
||||
value.name,
|
||||
Call(List.empty, List(Call.Export(value.name, value.`type`)))
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -44,7 +44,6 @@ object ArgumentGetter {
|
||||
|
||||
val ServiceId = "getDataSrv"
|
||||
|
||||
def apply(value: VarModel, arg: scalajs.js.Dynamic): ArgumentGetter = {
|
||||
def apply(value: VarModel, arg: scalajs.js.Dynamic): ArgumentGetter =
|
||||
ArgumentGetter(ServiceId, value, arg)
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,8 @@ package aqua.builder
|
||||
import aqua.backend.{ArgDefinition, PrimitiveType, ServiceDef, ServiceFunctionDef, VoidType}
|
||||
import aqua.io.OutputPrinter
|
||||
import aqua.js.{CallJsFunction, CallServiceHandler, FluencePeer}
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.func.raw.CallServiceTag
|
||||
import aqua.model.{LiteralModel, VarModel}
|
||||
import aqua.raw.ops.{Call, CallServiceTag}
|
||||
import aqua.raw.value.{LiteralRaw, VarRaw}
|
||||
|
||||
import scala.scalajs.js
|
||||
import scala.scalajs.js.{Dynamic, JSON}
|
||||
@ -14,13 +13,12 @@ import scala.scalajs.js.{Dynamic, JSON}
|
||||
class Console(serviceId: String, fnName: String, resultNames: List[String])
|
||||
extends ServiceFunction {
|
||||
|
||||
def callTag(variables: List[VarModel]): CallServiceTag = {
|
||||
def callTag(variables: List[VarRaw]): CallServiceTag =
|
||||
CallServiceTag(
|
||||
LiteralModel.quote(serviceId),
|
||||
LiteralRaw.quote(serviceId),
|
||||
fnName,
|
||||
Call(variables, Nil)
|
||||
)
|
||||
}
|
||||
|
||||
def registerService(peer: FluencePeer): Unit = {
|
||||
CallJsFunction.registerService(
|
||||
|
@ -3,9 +3,9 @@ package aqua.builder
|
||||
import aqua.backend.{ServiceDef, ServiceFunctionDef, VoidType}
|
||||
import aqua.io.OutputPrinter
|
||||
import aqua.js.{CallJsFunction, FluencePeer}
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.func.raw.CallServiceTag
|
||||
import aqua.model.{LiteralModel, VarModel}
|
||||
import aqua.raw.ops.{Call, CallServiceTag}
|
||||
import aqua.raw.value.LiteralRaw
|
||||
|
||||
import scala.concurrent.Promise
|
||||
import scala.scalajs.js
|
||||
@ -21,7 +21,7 @@ case class Finisher private (
|
||||
|
||||
def callTag(): CallServiceTag = {
|
||||
CallServiceTag(
|
||||
LiteralModel.quote(serviceId),
|
||||
LiteralRaw.quote(serviceId),
|
||||
fnName,
|
||||
Call(Nil, Nil)
|
||||
)
|
||||
|
@ -10,6 +10,7 @@ import aqua.builder.IPFSUploader
|
||||
import aqua.files.AquaFilesIO
|
||||
import aqua.ipfs.js.IpfsApi
|
||||
import aqua.model.LiteralModel
|
||||
import aqua.raw.value.LiteralRaw
|
||||
import aqua.run.RunCommand.createKeyPair
|
||||
import aqua.run.{GeneralRunOptions, RunCommand, RunConfig, RunOpts}
|
||||
import cats.effect.{Concurrent, ExitCode, Resource, Sync}
|
||||
@ -59,7 +60,7 @@ object IpfsOpts extends Logging {
|
||||
UploadFuncName,
|
||||
Path(IpfsAquaPath),
|
||||
Nil,
|
||||
LiteralModel.quote(path) :: Nil,
|
||||
LiteralRaw.quote(path) :: Nil,
|
||||
Map.empty,
|
||||
ipfsUploader :: Nil
|
||||
)
|
||||
|
@ -5,6 +5,7 @@ import aqua.builder.IPFSUploader
|
||||
import aqua.files.AquaFilesIO
|
||||
import aqua.ipfs.IpfsOpts.{pathOpt, IpfsAquaPath, UploadFuncName}
|
||||
import aqua.model.{LiteralModel, ValueModel}
|
||||
import aqua.raw.value.{LiteralRaw, ValueRaw}
|
||||
import aqua.run.{GeneralRunOptions, RunCommand, RunConfig, RunOpts}
|
||||
import cats.effect.ExitCode
|
||||
import cats.effect.kernel.Async
|
||||
@ -84,7 +85,7 @@ object NetworkOpts {
|
||||
ListInterfacesByPeerFuncName,
|
||||
Path(NetworkAquaPath),
|
||||
Nil,
|
||||
peer.map(LiteralModel.quote).getOrElse(LiteralModel.initPeerId) :: Nil
|
||||
peer.map(LiteralRaw.quote).getOrElse(ValueRaw.InitPeerId) :: Nil
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -116,7 +117,7 @@ object NetworkOpts {
|
||||
GetInterfaceFuncName,
|
||||
Path(NetworkAquaPath),
|
||||
Nil,
|
||||
LiteralModel.quote(serviceId) :: Nil
|
||||
LiteralRaw.quote(serviceId) :: Nil
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -132,7 +133,7 @@ object NetworkOpts {
|
||||
GetModuleInterfaceFuncName,
|
||||
Path(NetworkAquaPath),
|
||||
Nil,
|
||||
LiteralModel.quote(serviceId) :: Nil
|
||||
LiteralRaw.quote(serviceId) :: Nil
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -11,14 +11,13 @@ import aqua.compiler.{AquaCompiled, AquaCompiler}
|
||||
import aqua.files.{AquaFileSources, AquaFilesIO, FileModuleId}
|
||||
import aqua.io.{AquaFileError, OutputPrinter}
|
||||
import aqua.js.*
|
||||
import aqua.model.func.raw.{CallArrowTag, CallServiceTag, FuncOp, FuncOps}
|
||||
import aqua.model.func.{Call, FuncCallable}
|
||||
import aqua.model.transform.res.{AquaRes, FuncRes}
|
||||
import aqua.model.transform.{Transform, TransformConfig}
|
||||
import aqua.model.{AquaContext, LiteralModel, ValueModel, VarModel}
|
||||
import aqua.parser.expr.func.CallArrowExpr
|
||||
import aqua.parser.lexer.Literal
|
||||
import aqua.parser.lift.FileSpan
|
||||
import aqua.raw.AquaContext
|
||||
import aqua.raw.value.ValueRaw
|
||||
import aqua.run.RunConfig
|
||||
import aqua.types.*
|
||||
import cats.data.*
|
||||
@ -33,6 +32,7 @@ import cats.syntax.list.*
|
||||
import cats.syntax.monad.*
|
||||
import cats.syntax.show.*
|
||||
import cats.syntax.traverse.*
|
||||
import aqua.raw.arrow.FuncArrow
|
||||
import cats.{~>, Id, Monad}
|
||||
import fs2.io.file.{Files, Path}
|
||||
import scribe.Logging
|
||||
@ -54,7 +54,7 @@ object RunCommand extends Logging {
|
||||
}.getOrElse(Future.successful(None))
|
||||
}
|
||||
|
||||
private def findFunction(contexts: Chain[AquaContext], funcName: String): Option[FuncCallable] =
|
||||
private def findFunction(contexts: Chain[AquaContext], funcName: String): Option[FuncArrow] =
|
||||
contexts
|
||||
.flatMap(_.exports.map(e => Chain.fromSeq(e.funcs.values.toList)).getOrElse(Chain.empty))
|
||||
.find(_.funcName == funcName)
|
||||
@ -67,7 +67,7 @@ object RunCommand extends Logging {
|
||||
*/
|
||||
def run[F[_]: Files: AquaIO: Async](
|
||||
func: String,
|
||||
args: List[ValueModel],
|
||||
args: List[ValueRaw],
|
||||
input: Path,
|
||||
imports: List[Path],
|
||||
runConfig: RunConfig,
|
||||
|
@ -12,6 +12,7 @@ import Validated.{invalid, invalidNec, valid, validNec, validNel}
|
||||
import aqua.builder.{ArgumentGetter, ServiceFunction}
|
||||
import aqua.files.AquaFilesIO
|
||||
import aqua.model.transform.TransformConfig
|
||||
import aqua.raw.value.{LiteralRaw, ValueRaw, VarRaw}
|
||||
import cats.effect.kernel.Async
|
||||
import cats.effect.{Concurrent, ExitCode, IO}
|
||||
import cats.syntax.applicative.*
|
||||
@ -97,7 +98,7 @@ object RunOpts extends Logging {
|
||||
}.leftMap(t => NonEmptyList.one("Data isn't a valid JSON: " + t.getMessage))
|
||||
}
|
||||
|
||||
val funcOpt: Opts[(String, List[ValueModel])] =
|
||||
val funcOpt: Opts[(String, List[ValueRaw])] =
|
||||
Opts
|
||||
.option[String]("func", "Function to call with args", "f")
|
||||
.mapValidated { str =>
|
||||
@ -107,9 +108,10 @@ object RunOpts extends Logging {
|
||||
|
||||
val args = expr.args.collect {
|
||||
case Literal(value, ts) =>
|
||||
LiteralModel(value, ts)
|
||||
LiteralRaw(value, ts)
|
||||
case VarLambda(name, _) =>
|
||||
VarModel(name.value, BottomType)
|
||||
// TODO why BottomType?
|
||||
VarRaw(name.value, BottomType)
|
||||
}
|
||||
|
||||
validNel((expr.funcName.value, args))
|
||||
@ -121,7 +123,7 @@ object RunOpts extends Logging {
|
||||
// checks if data is presented if there is non-literals in function arguments
|
||||
// creates services to add this data into a call
|
||||
def checkDataGetServices(
|
||||
args: List[ValueModel],
|
||||
args: List[ValueRaw],
|
||||
data: Option[js.Dynamic]
|
||||
): ValidatedNec[String, Map[String, ArgumentGetter]] = {
|
||||
val vars = args.collect { case v @ VarModel(_, _, _) =>
|
||||
@ -158,7 +160,7 @@ object RunOpts extends Logging {
|
||||
// Default transform config with `onPeer` constant
|
||||
def transformConfigWithOnPeer(onPeer: Option[String]) =
|
||||
TransformConfig(constants =
|
||||
onPeer.map(s => TransformConfig.Const(OnPeerConst, LiteralModel.quote(s))).toList
|
||||
onPeer.map(s => TransformConfig.Const(OnPeerConst, LiteralRaw.quote(s))).toList
|
||||
)
|
||||
|
||||
/**
|
||||
@ -177,7 +179,7 @@ object RunOpts extends Logging {
|
||||
funcName: String,
|
||||
inputPath: Path,
|
||||
imports: List[Path] = Nil,
|
||||
args: List[ValueModel] = Nil,
|
||||
args: List[ValueRaw] = Nil,
|
||||
argumentGetters: Map[String, ArgumentGetter] = Map.empty,
|
||||
services: List[ServiceFunction] = Nil
|
||||
)(implicit
|
||||
|
@ -5,9 +5,10 @@ import aqua.backend.air.FuncAirGen
|
||||
import aqua.builder.{ArgumentGetter, Console, Finisher}
|
||||
import aqua.io.OutputPrinter
|
||||
import aqua.model.{ValueModel, VarModel}
|
||||
import aqua.model.func.{Call, FuncCallable}
|
||||
import aqua.model.func.raw.{CallArrowTag, FuncOp, FuncOps}
|
||||
import aqua.model.transform.{Transform, TransformConfig}
|
||||
import aqua.raw.arrow.FuncArrow
|
||||
import aqua.raw.ops.{Call, CallArrowTag, FuncOp, FuncOps}
|
||||
import aqua.raw.value.{ValueRaw, VarRaw}
|
||||
import aqua.types.{ArrowType, BoxType, NilType, Type}
|
||||
import cats.data.{Validated, ValidatedNec}
|
||||
import cats.effect.kernel.Async
|
||||
@ -17,18 +18,17 @@ import scala.concurrent.ExecutionContext
|
||||
import scala.scalajs.js
|
||||
|
||||
class Runner(
|
||||
funcName: String,
|
||||
funcCallable: FuncCallable,
|
||||
args: List[ValueModel],
|
||||
config: RunConfig,
|
||||
transformConfig: TransformConfig
|
||||
funcName: String,
|
||||
funcCallable: FuncArrow,
|
||||
args: List[ValueRaw],
|
||||
config: RunConfig,
|
||||
transformConfig: TransformConfig
|
||||
) {
|
||||
|
||||
def resultVariableNames(funcCallable: FuncCallable, name: String): List[String] = {
|
||||
def resultVariableNames(funcCallable: FuncArrow, name: String): List[String] =
|
||||
funcCallable.arrowType.codomain.toList.zipWithIndex.map { case (t, idx) =>
|
||||
name + idx
|
||||
}
|
||||
}
|
||||
|
||||
// Wraps function with necessary services, registers services and calls wrapped function with FluenceJS
|
||||
def run[F[_]: Async]()(implicit ec: ExecutionContext): ValidatedNec[String, F[Unit]] = {
|
||||
@ -54,11 +54,11 @@ class Runner(
|
||||
|
||||
// Generates air from function, register all services and make a call through FluenceJS
|
||||
private def genAirAndMakeCall[F[_]: Async](
|
||||
wrapped: FuncCallable,
|
||||
consoleService: Console,
|
||||
finisherService: Finisher
|
||||
wrapped: FuncArrow,
|
||||
consoleService: Console,
|
||||
finisherService: Finisher
|
||||
)(implicit ec: ExecutionContext): F[Unit] = {
|
||||
val funcRes = Transform.fn(wrapped, transformConfig)
|
||||
val funcRes = Transform.funcRes(wrapped, transformConfig)
|
||||
val definitions = FunctionDef(funcRes)
|
||||
|
||||
val air = FuncAirGen(funcRes).generate.show
|
||||
@ -108,7 +108,7 @@ class Runner(
|
||||
private def wrapCall(
|
||||
consoleService: Console,
|
||||
finisherService: Finisher
|
||||
): ValidatedNec[String, FuncCallable] = {
|
||||
): ValidatedNec[String, FuncArrow] = {
|
||||
// pass results to a printing service if an input function returns a result
|
||||
// otherwise just call it
|
||||
val body = funcCallable.arrowType.codomain.toList match {
|
||||
@ -117,7 +117,7 @@ class Runner(
|
||||
case types =>
|
||||
val (variables, exports) = types.zipWithIndex.map { case (t, idx) =>
|
||||
val name = config.resultName + idx
|
||||
(VarModel(name, t), Call.Export(name, t))
|
||||
(VarRaw(name, t), Call.Export(name, t))
|
||||
}.unzip
|
||||
val callFuncTag =
|
||||
CallArrowTag(funcName, Call(args, exports))
|
||||
@ -144,7 +144,7 @@ class Runner(
|
||||
gettersV.map { getters =>
|
||||
val gettersTags = getters.map(s => FuncOp.leaf(s.callTag()))
|
||||
|
||||
FuncCallable(
|
||||
FuncArrow(
|
||||
config.functionWrapperName,
|
||||
FuncOps.seq((gettersTags :+ body :+ FuncOp.leaf(finisherServiceTag)): _*),
|
||||
// no arguments and returns nothing
|
||||
|
@ -5,6 +5,7 @@ import aqua.model.LiteralModel
|
||||
import aqua.model.transform.TransformConfig
|
||||
import aqua.parser.expr.ConstantExpr
|
||||
import aqua.parser.lift.LiftParser
|
||||
import aqua.raw.value.LiteralRaw
|
||||
import cats.data.Validated.{Invalid, Valid}
|
||||
import cats.data.{NonEmptyList, Validated, ValidatedNec, ValidatedNel}
|
||||
import cats.effect.kernel.Async
|
||||
@ -126,7 +127,7 @@ object AppOpts {
|
||||
.fold(
|
||||
Validated.validNel[String, List[TransformConfig.Const]](parsed.collect {
|
||||
case Right(v) =>
|
||||
TransformConfig.Const(v._1.value, LiteralModel(v._2.value, v._2.ts))
|
||||
TransformConfig.Const(v._1.value, LiteralRaw(v._2.value, v._2.ts))
|
||||
})
|
||||
) { errors =>
|
||||
val errorMsgs = errors.map(str => s"Invalid constant definition '$str'.")
|
||||
|
@ -2,11 +2,11 @@ package aqua.compiler
|
||||
|
||||
import aqua.backend.Backend
|
||||
import aqua.linker.Linker
|
||||
import aqua.model.AquaContext
|
||||
import aqua.model.transform.TransformConfig
|
||||
import aqua.model.transform.res.AquaRes
|
||||
import aqua.parser.lift.{LiftParser, Span}
|
||||
import aqua.parser.{Ast, ParserError}
|
||||
import aqua.raw.AquaContext
|
||||
import aqua.semantics.Semantics
|
||||
import aqua.semantics.header.HeaderSem
|
||||
import cats.data.*
|
||||
|
@ -1,6 +1,6 @@
|
||||
package aqua.compiler
|
||||
|
||||
import aqua.model.AquaContext
|
||||
import aqua.raw.AquaContext
|
||||
|
||||
case class AquaProcessed[I](id: I, context: AquaContext) {
|
||||
def hasOutput: Boolean = context.funcs.nonEmpty || context.services.nonEmpty
|
||||
|
@ -1,7 +1,8 @@
|
||||
package aqua.model
|
||||
package aqua.raw
|
||||
|
||||
import aqua.model.func.raw.{CallServiceTag, FuncOp}
|
||||
import aqua.model.func.{ArgsCall, FuncCallable, FuncModel}
|
||||
import aqua.raw.arrow.{ArgsCall, FuncArrow, FuncRaw}
|
||||
import aqua.raw.ops.{CallServiceTag, FuncOp}
|
||||
import aqua.raw.value.ValueRaw
|
||||
import aqua.types.{StructType, Type}
|
||||
import cats.Monoid
|
||||
import cats.data.NonEmptyMap
|
||||
@ -13,15 +14,15 @@ import scribe.Logging
|
||||
import scala.collection.immutable.SortedMap
|
||||
|
||||
case class AquaContext(
|
||||
module: Option[String],
|
||||
declares: Set[String],
|
||||
exports: Option[AquaContext],
|
||||
funcs: Map[String, FuncCallable],
|
||||
types: Map[String, Type],
|
||||
values: Map[String, ValueModel],
|
||||
abilities: Map[String, AquaContext],
|
||||
// TODO: merge this with abilities, when have ability resolution variance
|
||||
services: Map[String, ServiceModel]
|
||||
module: Option[String],
|
||||
declares: Set[String],
|
||||
exports: Option[AquaContext],
|
||||
funcs: Map[String, FuncArrow],
|
||||
types: Map[String, Type],
|
||||
values: Map[String, ValueRaw],
|
||||
abilities: Map[String, AquaContext],
|
||||
// TODO: merge this with abilities, when have ability resolution variance
|
||||
services: Map[String, ServiceRaw]
|
||||
) {
|
||||
|
||||
private def prefixFirst[T](prefix: String, pair: (String, T)): (String, T) =
|
||||
@ -69,21 +70,21 @@ case class AquaContext(
|
||||
}
|
||||
.map(prefixFirst(prefix, _))
|
||||
|
||||
def allFuncs(prefix: String = ""): Map[String, FuncCallable] =
|
||||
def allFuncs(prefix: String = ""): Map[String, FuncArrow] =
|
||||
abilities
|
||||
.foldLeft(funcs) { case (ts, (k, v)) =>
|
||||
ts ++ v.allFuncs(k + ".")
|
||||
}
|
||||
.map(prefixFirst(prefix, _))
|
||||
|
||||
def allValues(prefix: String = ""): Map[String, ValueModel] =
|
||||
def allValues(prefix: String = ""): Map[String, ValueRaw] =
|
||||
abilities
|
||||
.foldLeft(values) { case (ts, (k, v)) =>
|
||||
ts ++ v.allValues(k + ".")
|
||||
}
|
||||
.map(prefixFirst(prefix, _))
|
||||
|
||||
def allServices(prefix: String = ""): Map[String, ServiceModel] =
|
||||
def allServices(prefix: String = ""): Map[String, ServiceRaw] =
|
||||
abilities
|
||||
.foldLeft(services) { case (ts, (k, v)) =>
|
||||
ts ++ v.allServices(k + ".")
|
||||
@ -142,7 +143,7 @@ object AquaContext extends Logging {
|
||||
}
|
||||
}
|
||||
|
||||
def fromServiceModel(sm: ServiceModel, serviceId: ValueModel): AquaContext =
|
||||
def fromService(sm: ServiceRaw, serviceId: ValueRaw): AquaContext =
|
||||
AquaContext(
|
||||
module = Some(sm.name),
|
||||
declares = sm.`type`.fields.toNel.map(_._1).toList.toSet,
|
||||
@ -150,12 +151,12 @@ object AquaContext extends Logging {
|
||||
funcs = sm.arrows.toSortedMap.map { case (fnName, arrowType) =>
|
||||
val (args, call, ret) = ArgsCall.arrowToArgsCallRet(arrowType)
|
||||
fnName ->
|
||||
FuncCallable(
|
||||
FuncArrow(
|
||||
fnName,
|
||||
// TODO: capture ability resolution, get ID from the call context
|
||||
FuncOp.leaf(CallServiceTag(serviceId, fnName, call)),
|
||||
arrowType,
|
||||
ret.map(_.model),
|
||||
ret.map(_.toRaw),
|
||||
Map.empty,
|
||||
Map.empty
|
||||
)
|
||||
@ -166,12 +167,12 @@ object AquaContext extends Logging {
|
||||
services = Map.empty
|
||||
)
|
||||
|
||||
def fromScriptModel(sm: ScriptModel, init: AquaContext)(implicit
|
||||
def fromRawContext(rawContext: ContextRaw, init: AquaContext)(implicit
|
||||
aqum: Semigroup[AquaContext]
|
||||
): AquaContext =
|
||||
sm.models
|
||||
rawContext.parts
|
||||
.foldLeft((init, blank)) {
|
||||
case ((ctx, exportContext), c: ConstantModel) =>
|
||||
case ((ctx, exportContext), c: ConstantRaw) =>
|
||||
val add =
|
||||
blank
|
||||
.copy(values =
|
||||
@ -179,21 +180,20 @@ object AquaContext extends Logging {
|
||||
else Map(c.name -> c.value.resolveWith(ctx.values))
|
||||
)
|
||||
(ctx |+| add, exportContext |+| add)
|
||||
case ((ctx, exportContext), func: FuncModel) =>
|
||||
case ((ctx, exportContext), func: FuncRaw) =>
|
||||
val fr = func.capture(ctx.allFuncs(), ctx.allValues())
|
||||
val add =
|
||||
blank.copy(funcs = Map(func.name -> fr))
|
||||
(ctx |+| add, exportContext |+| add)
|
||||
case ((ctx, exportContext), t: TypeModel) =>
|
||||
case ((ctx, exportContext), t: TypeRaw) =>
|
||||
val add =
|
||||
blank.copy(types = Map(t.name -> t.`type`))
|
||||
(ctx |+| add, exportContext |+| add)
|
||||
case ((ctx, exportContext), m: ServiceModel) =>
|
||||
case ((ctx, exportContext), m: ServiceRaw) =>
|
||||
val add =
|
||||
blank
|
||||
.copy(
|
||||
abilities =
|
||||
m.defaultId.fold(Map.empty)(id => Map(m.name -> fromServiceModel(m, id))),
|
||||
abilities = m.defaultId.fold(Map.empty)(id => Map(m.name -> fromService(m, id))),
|
||||
services = Map(m.name -> m)
|
||||
)
|
||||
(ctx |+| add, exportContext |+| add)
|
5
model/raw/src/main/scala/aqua/raw/ConstantRaw.scala
Normal file
5
model/raw/src/main/scala/aqua/raw/ConstantRaw.scala
Normal file
@ -0,0 +1,5 @@
|
||||
package aqua.raw
|
||||
|
||||
import aqua.raw.value.ValueRaw
|
||||
|
||||
case class ConstantRaw(name: String, value: ValueRaw, allowOverrides: Boolean) extends Raw
|
32
model/raw/src/main/scala/aqua/raw/ContextRaw.scala
Normal file
32
model/raw/src/main/scala/aqua/raw/ContextRaw.scala
Normal file
@ -0,0 +1,32 @@
|
||||
package aqua.raw
|
||||
|
||||
import cats.Monoid
|
||||
import cats.data.Chain
|
||||
import aqua.raw.arrow.FuncRaw
|
||||
|
||||
case class ContextRaw(
|
||||
parts: Chain[Raw] = Chain.empty
|
||||
) extends Raw
|
||||
|
||||
object ContextRaw {
|
||||
implicit object CRMonoid extends Monoid[ContextRaw] {
|
||||
override def empty: ContextRaw = ContextRaw()
|
||||
|
||||
override def combine(x: ContextRaw, y: ContextRaw): ContextRaw =
|
||||
ContextRaw(
|
||||
x.parts ++ y.parts
|
||||
)
|
||||
}
|
||||
|
||||
def contextPart(raw: Raw): ContextRaw = raw match {
|
||||
case cr: ContextRaw => cr
|
||||
case _ =>
|
||||
ContextRaw(Chain.one(raw).filter {
|
||||
case _: FuncRaw => true
|
||||
case _: ServiceRaw => true
|
||||
case _: TypeRaw => true
|
||||
case _: ConstantRaw => true
|
||||
case _ => false
|
||||
})
|
||||
}
|
||||
}
|
40
model/raw/src/main/scala/aqua/raw/Raw.scala
Normal file
40
model/raw/src/main/scala/aqua/raw/Raw.scala
Normal file
@ -0,0 +1,40 @@
|
||||
package aqua.raw
|
||||
|
||||
import aqua.raw.ops.FuncOp
|
||||
import cats.Semigroup
|
||||
|
||||
trait Raw
|
||||
|
||||
object Raw {
|
||||
|
||||
def error(log: String): Raw = Empty(log)
|
||||
def empty(log: String): Raw = Empty(log)
|
||||
|
||||
case class Empty(log: String) extends Raw
|
||||
|
||||
implicit object MergeRaw extends Semigroup[Raw] {
|
||||
|
||||
import ContextRaw.CRMonoid
|
||||
import FuncOp.FuncOpSemigroup
|
||||
import ContextRaw.contextPart
|
||||
|
||||
override def combine(x: Raw, y: Raw): Raw =
|
||||
(x, y) match {
|
||||
case (l: FuncOp, r: FuncOp) =>
|
||||
FuncOpSemigroup.combine(l, r)
|
||||
case (l: ContextRaw, r: ContextRaw) =>
|
||||
CRMonoid.combine(l, r)
|
||||
|
||||
case (l: Empty, r: Empty) => Empty(l.log + " |+| " + r.log)
|
||||
case (_: Empty, r) => r
|
||||
case (l, _: Empty) => l
|
||||
|
||||
case (l, r) =>
|
||||
CRMonoid.combine(
|
||||
contextPart(l),
|
||||
contextPart(r)
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,13 @@
|
||||
package aqua.model
|
||||
package aqua.raw
|
||||
|
||||
import aqua.types.{ArrowType, StructType}
|
||||
import cats.data.NonEmptyMap
|
||||
import aqua.raw.value.ValueRaw
|
||||
|
||||
case class ServiceModel(
|
||||
case class ServiceRaw(
|
||||
name: String,
|
||||
arrows: NonEmptyMap[String, ArrowType],
|
||||
defaultId: Option[ValueModel]
|
||||
) extends Model {
|
||||
defaultId: Option[ValueRaw]
|
||||
) extends Raw {
|
||||
def `type`: StructType = StructType(name, arrows)
|
||||
}
|
5
model/raw/src/main/scala/aqua/raw/TypeRaw.scala
Normal file
5
model/raw/src/main/scala/aqua/raw/TypeRaw.scala
Normal file
@ -0,0 +1,5 @@
|
||||
package aqua.raw
|
||||
|
||||
import aqua.types.Type
|
||||
|
||||
case class TypeRaw(name: String, `type`: Type) extends Raw
|
@ -1,26 +1,33 @@
|
||||
package aqua.model.func
|
||||
package aqua.raw.arrow
|
||||
|
||||
import aqua.model.{ValueModel, VarModel}
|
||||
import aqua.types.{ArrowType, DataType, ProductType, Type}
|
||||
import aqua.raw.ops.Call
|
||||
import aqua.raw.value.{ValueRaw, VarRaw}
|
||||
import aqua.types.{ArrowType, DataType, ProductType, StreamType, Type}
|
||||
|
||||
/**
|
||||
* Wraps argument definitions of a function, along with values provided when this function is called
|
||||
* @param args Argument definitions
|
||||
*
|
||||
* @param args Argument definitions
|
||||
* @param callWith Values provided for arguments
|
||||
*/
|
||||
case class ArgsCall(args: ProductType, callWith: List[ValueModel]) {
|
||||
case class ArgsCall(args: ProductType, callWith: List[ValueRaw]) {
|
||||
// Both arguments (arg names and types how they seen from the function body)
|
||||
// and values (value models and types how they seen on the call site)
|
||||
lazy val zipped: List[((String, Type), ValueModel)] = args.toLabelledList() zip callWith
|
||||
lazy val zipped: List[((String, Type), ValueRaw)] = args.toLabelledList() zip callWith
|
||||
|
||||
lazy val dataArgs: Map[String, ValueModel] =
|
||||
lazy val dataArgs: Map[String, ValueRaw] =
|
||||
zipped.collect { case ((name, _: DataType), value) =>
|
||||
name -> value
|
||||
}.toMap
|
||||
|
||||
def arrowArgs(arrowsInScope: Map[String, FuncCallable]): Map[String, FuncCallable] =
|
||||
lazy val streamArgs: Map[String, VarRaw] =
|
||||
dataArgs.collect { case (k, vr @ VarRaw(n, StreamType(_), _)) =>
|
||||
(k, vr)
|
||||
}
|
||||
|
||||
def arrowArgs(arrowsInScope: Map[String, FuncArrow]): Map[String, FuncArrow] =
|
||||
zipped.collect {
|
||||
case ((name, _: ArrowType), VarModel(value, _, _)) if arrowsInScope.contains(value) =>
|
||||
case ((name, _: ArrowType), VarRaw(value, _, _)) if arrowsInScope.contains(value) =>
|
||||
name -> arrowsInScope(value)
|
||||
}.toMap
|
||||
}
|
||||
@ -37,7 +44,7 @@ object ArgsCall {
|
||||
|
||||
val call = Call(
|
||||
argNamesTypes.map { case (a, t) =>
|
||||
VarModel(a, t)
|
||||
VarRaw(a, t)
|
||||
},
|
||||
res
|
||||
)
|
12
model/raw/src/main/scala/aqua/raw/arrow/ArrowRaw.scala
Normal file
12
model/raw/src/main/scala/aqua/raw/arrow/ArrowRaw.scala
Normal file
@ -0,0 +1,12 @@
|
||||
package aqua.raw.arrow
|
||||
|
||||
import aqua.raw.ops.FuncOp
|
||||
import aqua.types.ArrowType
|
||||
import aqua.raw.Raw
|
||||
import aqua.raw.value.ValueRaw
|
||||
|
||||
case class ArrowRaw(
|
||||
`type`: ArrowType,
|
||||
ret: List[ValueRaw],
|
||||
body: FuncOp
|
||||
) extends Raw
|
20
model/raw/src/main/scala/aqua/raw/arrow/FuncArrow.scala
Normal file
20
model/raw/src/main/scala/aqua/raw/arrow/FuncArrow.scala
Normal file
@ -0,0 +1,20 @@
|
||||
package aqua.raw.arrow
|
||||
|
||||
import aqua.raw.Raw
|
||||
import aqua.raw.ops.FuncOp
|
||||
import aqua.raw.value.ValueRaw
|
||||
import aqua.types.{ArrowType, Type}
|
||||
|
||||
case class FuncArrow(
|
||||
funcName: String,
|
||||
body: FuncOp,
|
||||
arrowType: ArrowType,
|
||||
ret: List[ValueRaw],
|
||||
capturedArrows: Map[String, FuncArrow],
|
||||
capturedValues: Map[String, ValueRaw]
|
||||
) extends Raw {
|
||||
|
||||
lazy val args: List[(String, Type)] = arrowType.domain.toLabelledList()
|
||||
lazy val argNames: List[String] = args.map(_._1)
|
||||
|
||||
}
|
17
model/raw/src/main/scala/aqua/raw/arrow/FuncRaw.scala
Normal file
17
model/raw/src/main/scala/aqua/raw/arrow/FuncRaw.scala
Normal file
@ -0,0 +1,17 @@
|
||||
package aqua.raw.arrow
|
||||
|
||||
import aqua.raw.value.ValueRaw
|
||||
import aqua.raw.Raw
|
||||
|
||||
case class FuncRaw(
|
||||
name: String,
|
||||
arrow: ArrowRaw
|
||||
) extends Raw {
|
||||
|
||||
def capture(
|
||||
arrows: Map[String, FuncArrow],
|
||||
constants: Map[String, ValueRaw]
|
||||
): FuncArrow =
|
||||
FuncArrow(name, arrow.body.fixXorPar, arrow.`type`, arrow.ret, arrows, constants)
|
||||
|
||||
}
|
36
model/raw/src/main/scala/aqua/raw/ops/Call.scala
Normal file
36
model/raw/src/main/scala/aqua/raw/ops/Call.scala
Normal file
@ -0,0 +1,36 @@
|
||||
package aqua.raw.ops
|
||||
|
||||
import aqua.raw.value.{ValueRaw, VarRaw}
|
||||
import aqua.types.{ArrowType, Type}
|
||||
|
||||
// TODO docs
|
||||
case class Call(args: List[ValueRaw], exportTo: List[Call.Export]) {
|
||||
|
||||
def mapValues(f: ValueRaw => ValueRaw): Call =
|
||||
Call(
|
||||
args.map(_.map(f)),
|
||||
exportTo
|
||||
)
|
||||
|
||||
// TODO docs
|
||||
def mapExport(f: String => String): Call = copy(exportTo = exportTo.map(_.mapName(f)))
|
||||
|
||||
def argVarNames: Set[String] = args.flatMap(_.usesVarNames).toSet
|
||||
|
||||
def arrowArgNames: Set[String] = args.collect { case VarRaw(m, _: ArrowType, _) =>
|
||||
m
|
||||
}.toSet
|
||||
|
||||
override def toString: String =
|
||||
s"[${args.mkString(" ")}]${exportTo.map(_.toRaw).map(" " + _).mkString(",")}"
|
||||
}
|
||||
|
||||
object Call {
|
||||
|
||||
// TODO docs
|
||||
case class Export(name: String, `type`: Type) {
|
||||
def mapName(f: String => String): Export = copy(f(name))
|
||||
|
||||
def toRaw: VarRaw = VarRaw(name, `type`)
|
||||
}
|
||||
}
|
122
model/raw/src/main/scala/aqua/raw/ops/FuncOp.scala
Normal file
122
model/raw/src/main/scala/aqua/raw/ops/FuncOp.scala
Normal file
@ -0,0 +1,122 @@
|
||||
package aqua.raw.ops
|
||||
|
||||
import aqua.raw.Raw
|
||||
import aqua.raw.ops
|
||||
import aqua.raw.value.{ValueRaw, VarRaw}
|
||||
import cats.Eval
|
||||
import cats.data.Chain
|
||||
import cats.free.Cofree
|
||||
import cats.instances.tuple.*
|
||||
import cats.kernel.Semigroup
|
||||
import cats.syntax.apply.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.traverse.*
|
||||
import cats.Monad
|
||||
import cats.data.State
|
||||
import cats.data.StateT
|
||||
|
||||
case class FuncOp(tree: Cofree[Chain, RawTag]) extends Raw {
|
||||
def head: RawTag = tree.head
|
||||
|
||||
lazy val isRightAssoc: Boolean = head match {
|
||||
case XorTag | ParTag => true
|
||||
case _: XorParTag => true
|
||||
case _ => false
|
||||
}
|
||||
|
||||
def cata[T](folder: (RawTag, Chain[T]) => Eval[T]): Eval[T] =
|
||||
Cofree.cata(tree)(folder)
|
||||
|
||||
def definesVarNames: Eval[Set[String]] = cata[Set[String]] { case (tag, acc) =>
|
||||
Eval.later(acc.foldLeft(tag.definesVarNames)(_ ++ _))
|
||||
}
|
||||
|
||||
def exportsVarNames: Eval[Set[String]] = cata[Set[String]] { case (tag, acc) =>
|
||||
Eval.later(acc.foldLeft(tag.exportsVarNames)(_ ++ _) -- tag.restrictsVarNames)
|
||||
}
|
||||
|
||||
// TODO: as it is used for checking of intersection, make it a lazy traverse with fail-fast
|
||||
def usesVarNames: Eval[Set[String]] = cata[Set[String]] { case (tag, acc) =>
|
||||
Eval.later(acc.foldLeft(tag.usesVarNames)(_ ++ _) -- tag.restrictsVarNames)
|
||||
}
|
||||
|
||||
def resolveValues(vals: Map[String, ValueRaw]): FuncOp =
|
||||
FuncOp(tree.map[RawTag](_.mapValues(_.resolveWith(vals))))
|
||||
|
||||
def rename(vals: Map[String, String]): FuncOp = {
|
||||
if (vals.isEmpty)
|
||||
this
|
||||
else
|
||||
FuncOp(
|
||||
tree.map[RawTag](op => op.mapValues(_.renameVars(vals)).renameExports(vals))
|
||||
)
|
||||
}
|
||||
|
||||
def :+:(prev: FuncOp): FuncOp =
|
||||
FuncOp.RightAssocSemi.combine(prev, this)
|
||||
|
||||
// Function body must be fixed before function gets resolved
|
||||
def fixXorPar: FuncOp =
|
||||
ops.FuncOp(cata[Cofree[Chain, RawTag]] {
|
||||
case (XorParTag(left, right), _) =>
|
||||
Eval.now(
|
||||
FuncOps
|
||||
.par(
|
||||
FuncOp.wrap(XorTag, left),
|
||||
right
|
||||
)
|
||||
.tree
|
||||
)
|
||||
|
||||
case (head, tail) => Eval.now(Cofree(head, Eval.now(tail)))
|
||||
}.value)
|
||||
}
|
||||
|
||||
object FuncOp {
|
||||
type Tree = Cofree[Chain, RawTag]
|
||||
|
||||
def traverseS[S](cf: Tree, f: RawTag => State[S, Tree]): State[S, Tree] = for {
|
||||
headTree <- f(cf.head)
|
||||
tail <- StateT.liftF(cf.tail)
|
||||
tailTree <- tail.traverse(traverseS[S](_, f))
|
||||
} yield headTree.copy(tail = headTree.tail.map(_ ++ tailTree))
|
||||
|
||||
// Semigroup for foldRight processing
|
||||
object RightAssocSemi extends Semigroup[FuncOp] {
|
||||
|
||||
override def combine(x: FuncOp, y: FuncOp): FuncOp = (x.tree.head, y.tree.head) match {
|
||||
case (_: ParGroupTag, ParTag) =>
|
||||
FuncOp(y.tree.copy(tail = (x.tree.tail, y.tree.tail).mapN(_ ++ _)))
|
||||
case (XorTag, XorTag) =>
|
||||
FuncOp(y.tree.copy(tail = (x.tree.tail, y.tree.tail).mapN(_ ++ _)))
|
||||
case (XorTag.LeftBiased, XorTag) =>
|
||||
wrap(SeqTag, FuncOp(y.tree.copy(tail = (x.tree.tail, y.tree.tail).mapN(_ ++ _))))
|
||||
case (XorTag, ParTag) => FuncOp(Cofree[Chain, RawTag](XorParTag(x, y), Eval.now(Chain.empty)))
|
||||
case (_, ParTag | XorTag) =>
|
||||
wrap(SeqTag, FuncOp(y.tree.copy(tail = y.tree.tail.map(_.prepend(x.tree)))))
|
||||
case (_, XorParTag(xor, par)) =>
|
||||
combine(combine(x, xor), par)
|
||||
case _ => FuncOpSemigroup.combine(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
implicit object FuncOpSemigroup extends Semigroup[FuncOp] {
|
||||
|
||||
override def combine(x: FuncOp, y: FuncOp): FuncOp = (x.tree.head, y.tree.head) match {
|
||||
case (_, XorParTag(xor, par)) => combine(combine(x, xor), par)
|
||||
case (XorParTag(xor, par), _) => combine(combine(xor, par), y)
|
||||
case (SeqTag, SeqTag) => FuncOp(y.tree.copy(tail = (x.tree.tail, y.tree.tail).mapN(_ ++ _)))
|
||||
case (_, SeqTag) => FuncOp(y.tree.copy(tail = y.tree.tail.map(_.prepend(x.tree))))
|
||||
case (SeqTag, _) => FuncOp(x.tree.copy(tail = x.tree.tail.map(_.append(y.tree))))
|
||||
case _ => node(SeqTag, Chain(x, y))
|
||||
}
|
||||
}
|
||||
|
||||
def leaf(tag: RawTag): FuncOp = FuncOp(Cofree[Chain, RawTag](tag, Eval.now(Chain.empty)))
|
||||
|
||||
def wrap(tag: RawTag, child: FuncOp): FuncOp = node(tag, Chain.one(child))
|
||||
|
||||
def node(tag: RawTag, children: Chain[FuncOp]): FuncOp =
|
||||
FuncOp(Cofree[Chain, RawTag](tag, Eval.later(children.map(_.tree))))
|
||||
}
|
@ -1,16 +1,15 @@
|
||||
package aqua.model.func.raw
|
||||
package aqua.raw.ops
|
||||
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.{LiteralModel, ValueModel}
|
||||
import aqua.raw.value.{LiteralRaw, ValueRaw}
|
||||
import cats.data.Chain
|
||||
import cats.free.Cofree
|
||||
|
||||
object FuncOps {
|
||||
|
||||
def noop: FuncOp =
|
||||
FuncOp.leaf(CallServiceTag(LiteralModel.quote("op"), "noop", Call(Nil, Nil)))
|
||||
FuncOp.leaf(CallServiceTag(LiteralRaw.quote("op"), "noop", Call(Nil, Nil)))
|
||||
|
||||
def pushToStream(what: ValueModel, to: Call.Export): FuncOp =
|
||||
def pushToStream(what: ValueRaw, to: Call.Export): FuncOp =
|
||||
FuncOp.leaf(
|
||||
PushToStreamTag(what, to)
|
||||
)
|
||||
@ -18,12 +17,12 @@ object FuncOps {
|
||||
/**
|
||||
* Canonicalizes [[what]] into [[to]], [[what]] is expected to be a stream
|
||||
*/
|
||||
def canonicalize(what: ValueModel, to: Call.Export): FuncOp =
|
||||
def canonicalize(what: ValueRaw, to: Call.Export): FuncOp =
|
||||
FuncOp.leaf(
|
||||
CanonicalizeTag(what, to)
|
||||
)
|
||||
|
||||
def callService(srvId: ValueModel, funcName: String, call: Call): FuncOp =
|
||||
def callService(srvId: ValueRaw, funcName: String, call: Call): FuncOp =
|
||||
FuncOp.leaf(
|
||||
CallServiceTag(
|
||||
srvId,
|
||||
@ -40,7 +39,7 @@ object FuncOps {
|
||||
)
|
||||
)
|
||||
|
||||
def onVia(on: ValueModel, via: Chain[ValueModel], wrap: FuncOp): FuncOp =
|
||||
def onVia(on: ValueRaw, via: Chain[ValueRaw], wrap: FuncOp): FuncOp =
|
||||
FuncOp.wrap(
|
||||
OnTag(on, via),
|
||||
wrap
|
||||
@ -78,7 +77,7 @@ object FuncOps {
|
||||
def xor(left: FuncOp, right: FuncOp): FuncOp =
|
||||
FuncOp.node(XorTag, Chain(left, right))
|
||||
|
||||
def fold(item: String, iter: ValueModel, op: FuncOp): FuncOp =
|
||||
def fold(item: String, iter: ValueRaw, op: FuncOp): FuncOp =
|
||||
FuncOp.wrap(
|
||||
ForTag(item, iter),
|
||||
op
|
233
model/raw/src/main/scala/aqua/raw/ops/RawTag.scala
Normal file
233
model/raw/src/main/scala/aqua/raw/ops/RawTag.scala
Normal file
@ -0,0 +1,233 @@
|
||||
package aqua.raw.ops
|
||||
|
||||
import aqua.raw.arrow.FuncRaw
|
||||
import aqua.raw.value.ValueRaw
|
||||
import cats.data.{Chain, NonEmptyList}
|
||||
|
||||
sealed trait RawTag {
|
||||
// What variable names this tag uses (children are not respected)
|
||||
def usesVarNames: Set[String] = Set.empty
|
||||
|
||||
// What var names are exported – can be used AFTER this tag is executed
|
||||
def exportsVarNames: Set[String] = Set.empty
|
||||
|
||||
// What var names are restricted only for children of this tag – CANNOT be used after this tag, only within
|
||||
def restrictsVarNames: Set[String] = Set.empty
|
||||
|
||||
// All variable names introduced by this tag
|
||||
def definesVarNames: Set[String] = exportsVarNames ++ restrictsVarNames
|
||||
|
||||
def mapValues(f: ValueRaw => ValueRaw): RawTag = this
|
||||
|
||||
def renameExports(map: Map[String, String]): RawTag = this
|
||||
|
||||
}
|
||||
|
||||
sealed trait NoExecTag extends RawTag
|
||||
|
||||
sealed trait GroupTag extends RawTag
|
||||
|
||||
sealed trait SeqGroupTag extends GroupTag
|
||||
|
||||
sealed trait ParGroupTag extends GroupTag
|
||||
|
||||
case object SeqTag extends SeqGroupTag
|
||||
|
||||
case object ParTag extends ParGroupTag {
|
||||
case object Detach extends ParGroupTag
|
||||
}
|
||||
|
||||
case object XorTag extends GroupTag {
|
||||
case object LeftBiased extends GroupTag
|
||||
}
|
||||
|
||||
case class XorParTag(xor: FuncOp, par: FuncOp) extends RawTag {
|
||||
// Collect all the used variable names
|
||||
override def usesVarNames: Set[String] = xor.usesVarNames.value ++ par.usesVarNames.value
|
||||
|
||||
override def exportsVarNames: Set[String] = xor.usesVarNames.value ++ par.usesVarNames.value
|
||||
}
|
||||
|
||||
case class OnTag(peerId: ValueRaw, via: Chain[ValueRaw]) extends SeqGroupTag {
|
||||
|
||||
override def usesVarNames: Set[String] =
|
||||
peerId.usesVarNames ++ via.iterator.flatMap(_.usesVarNames)
|
||||
|
||||
override def mapValues(f: ValueRaw => ValueRaw): RawTag =
|
||||
OnTag(peerId.map(f), via.map(_.map(f)))
|
||||
|
||||
override def toString: String =
|
||||
s"(on $peerId${if (via.nonEmpty) " via " + via.toList.mkString(" via ") else ""})"
|
||||
}
|
||||
|
||||
case class NextTag(item: String) extends RawTag {
|
||||
override def usesVarNames: Set[String] = Set(item)
|
||||
|
||||
override def renameExports(map: Map[String, String]): RawTag =
|
||||
copy(item = map.getOrElse(item, item))
|
||||
}
|
||||
|
||||
case class RestrictionTag(name: String, isStream: Boolean) extends SeqGroupTag {
|
||||
override def usesVarNames: Set[String] = Set.empty
|
||||
|
||||
override def restrictsVarNames: Set[String] = Set(name)
|
||||
|
||||
override def renameExports(map: Map[String, String]): RawTag =
|
||||
copy(name = map.getOrElse(name, name))
|
||||
}
|
||||
|
||||
case class MatchMismatchTag(left: ValueRaw, right: ValueRaw, shouldMatch: Boolean)
|
||||
extends SeqGroupTag {
|
||||
|
||||
override def usesVarNames: Set[String] =
|
||||
left.usesVarNames ++ right.usesVarNames
|
||||
|
||||
override def mapValues(f: ValueRaw => ValueRaw): RawTag =
|
||||
MatchMismatchTag(left.map(f), right.map(f), shouldMatch)
|
||||
}
|
||||
|
||||
case class ForTag(item: String, iterable: ValueRaw) extends SeqGroupTag {
|
||||
override def usesVarNames: Set[String] = iterable.usesVarNames
|
||||
|
||||
override def restrictsVarNames: Set[String] = Set(item)
|
||||
|
||||
override def mapValues(f: ValueRaw => ValueRaw): RawTag =
|
||||
ForTag(item, iterable.map(f))
|
||||
|
||||
override def renameExports(map: Map[String, String]): RawTag =
|
||||
copy(item = map.getOrElse(item, item))
|
||||
}
|
||||
|
||||
case class CallArrowTag(
|
||||
funcName: String,
|
||||
call: Call
|
||||
) extends RawTag {
|
||||
override def usesVarNames: Set[String] = call.argVarNames
|
||||
|
||||
override def exportsVarNames: Set[String] = call.exportTo.map(_.name).toSet
|
||||
|
||||
override def mapValues(f: ValueRaw => ValueRaw): RawTag =
|
||||
CallArrowTag(funcName, call.mapValues(f))
|
||||
|
||||
override def renameExports(map: Map[String, String]): RawTag =
|
||||
copy(call = call.mapExport(n => map.getOrElse(n, n)))
|
||||
}
|
||||
|
||||
case class DeclareStreamTag(
|
||||
value: ValueRaw
|
||||
) extends NoExecTag {
|
||||
override def usesVarNames: Set[String] = value.usesVarNames
|
||||
|
||||
override def mapValues(f: ValueRaw => ValueRaw): RawTag =
|
||||
DeclareStreamTag(value.map(f))
|
||||
}
|
||||
|
||||
case class AssignmentTag(
|
||||
value: ValueRaw,
|
||||
assignTo: String
|
||||
) extends NoExecTag {
|
||||
override def usesVarNames: Set[String] = Set(assignTo) ++ value.usesVarNames
|
||||
|
||||
override def renameExports(map: Map[String, String]): RawTag =
|
||||
copy(assignTo = map.getOrElse(assignTo, assignTo))
|
||||
|
||||
override def mapValues(f: ValueRaw => ValueRaw): RawTag =
|
||||
AssignmentTag(value.map(f), assignTo)
|
||||
}
|
||||
|
||||
case class ClosureTag(
|
||||
func: FuncRaw
|
||||
) extends NoExecTag {
|
||||
// TODO captured names are lost?
|
||||
override def usesVarNames: Set[String] = Set(func.name)
|
||||
|
||||
override def mapValues(f: ValueRaw => ValueRaw): RawTag =
|
||||
ClosureTag(
|
||||
func.copy(arrow =
|
||||
func.arrow.copy(
|
||||
ret = func.arrow.ret.map(_.map(f)),
|
||||
body = FuncOp(func.arrow.body.tree.map(_.mapValues(f)))
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
case class ReturnTag(
|
||||
values: NonEmptyList[ValueRaw]
|
||||
) extends NoExecTag {
|
||||
|
||||
override def mapValues(f: ValueRaw => ValueRaw): RawTag =
|
||||
ReturnTag(values.map(_.map(f)))
|
||||
}
|
||||
|
||||
object EmptyTag extends NoExecTag
|
||||
|
||||
case class AbilityIdTag(
|
||||
value: ValueRaw,
|
||||
service: String
|
||||
) extends NoExecTag {
|
||||
|
||||
override def mapValues(f: ValueRaw => ValueRaw): RawTag =
|
||||
AbilityIdTag(value.map(f), service)
|
||||
}
|
||||
|
||||
case class CallServiceTag(
|
||||
serviceId: ValueRaw,
|
||||
funcName: String,
|
||||
call: Call
|
||||
) extends RawTag {
|
||||
|
||||
override def usesVarNames: Set[String] = serviceId.usesVarNames ++ call.argVarNames
|
||||
|
||||
override def exportsVarNames: Set[String] = call.exportTo.map(_.name).toSet
|
||||
|
||||
override def mapValues(f: ValueRaw => ValueRaw): RawTag =
|
||||
CallServiceTag(serviceId.map(f), funcName, call.mapValues(f))
|
||||
|
||||
override def renameExports(map: Map[String, String]): RawTag =
|
||||
copy(call = call.mapExport(n => map.getOrElse(n, n)))
|
||||
|
||||
override def toString: String = s"(call _ ($serviceId $funcName) $call)"
|
||||
}
|
||||
|
||||
case class PushToStreamTag(operand: ValueRaw, exportTo: Call.Export) extends RawTag {
|
||||
override def usesVarNames: Set[String] = operand.usesVarNames
|
||||
|
||||
override def exportsVarNames: Set[String] = Set(exportTo.name)
|
||||
|
||||
override def mapValues(f: ValueRaw => ValueRaw): RawTag =
|
||||
PushToStreamTag(operand.map(f), exportTo)
|
||||
|
||||
override def renameExports(map: Map[String, String]): RawTag =
|
||||
copy(exportTo = exportTo.mapName(n => map.getOrElse(n, n)))
|
||||
|
||||
override def toString: String = s"(push $operand $exportTo)"
|
||||
}
|
||||
|
||||
case class CanonicalizeTag(operand: ValueRaw, exportTo: Call.Export) extends RawTag {
|
||||
override def usesVarNames: Set[String] = operand.usesVarNames
|
||||
|
||||
override def exportsVarNames: Set[String] = Set(exportTo.name)
|
||||
|
||||
override def mapValues(f: ValueRaw => ValueRaw): RawTag =
|
||||
CanonicalizeTag(operand.map(f), exportTo)
|
||||
|
||||
override def renameExports(map: Map[String, String]): RawTag =
|
||||
copy(exportTo = exportTo.mapName(n => map.getOrElse(n, n)))
|
||||
|
||||
override def toString: String = s"(can $operand $exportTo)"
|
||||
}
|
||||
|
||||
case class FlattenTag(operand: ValueRaw, assignTo: String) extends RawTag {
|
||||
override def usesVarNames: Set[String] = operand.usesVarNames
|
||||
|
||||
override def exportsVarNames: Set[String] = Set(assignTo)
|
||||
|
||||
override def mapValues(f: ValueRaw => ValueRaw): RawTag =
|
||||
FlattenTag(operand.map(f), assignTo)
|
||||
|
||||
override def renameExports(map: Map[String, String]): RawTag =
|
||||
copy(assignTo = map.getOrElse(assignTo, assignTo))
|
||||
|
||||
override def toString: String = s"(ap $operand $assignTo)"
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package aqua.model.func.raw
|
||||
package aqua.raw.ops
|
||||
|
||||
import cats.Show
|
||||
import cats.free.Cofree
|
32
model/raw/src/main/scala/aqua/raw/value/LambdaRaw.scala
Normal file
32
model/raw/src/main/scala/aqua/raw/value/LambdaRaw.scala
Normal file
@ -0,0 +1,32 @@
|
||||
package aqua.raw.value
|
||||
|
||||
import aqua.types.Type
|
||||
|
||||
sealed trait LambdaRaw {
|
||||
def `type`: Type
|
||||
|
||||
def usesVarNames: Set[String] = Set.empty
|
||||
|
||||
def map(f: ValueRaw => ValueRaw): LambdaRaw
|
||||
|
||||
def resolveWith(vals: Map[String, ValueRaw]): LambdaRaw = this
|
||||
|
||||
def renameVars(vals: Map[String, String]): LambdaRaw = this
|
||||
|
||||
}
|
||||
|
||||
case class IntoFieldRaw(field: String, `type`: Type) extends LambdaRaw {
|
||||
override def map(f: ValueRaw => ValueRaw): LambdaRaw = this
|
||||
}
|
||||
|
||||
case class IntoIndexRaw(idx: ValueRaw, `type`: Type) extends LambdaRaw {
|
||||
override def usesVarNames: Set[String] = idx.usesVarNames
|
||||
|
||||
override def map(f: ValueRaw => ValueRaw): LambdaRaw = IntoIndexRaw(f(idx), `type`)
|
||||
|
||||
override def resolveWith(vals: Map[String, ValueRaw]): LambdaRaw =
|
||||
IntoIndexRaw(idx.resolveWith(vals), `type`)
|
||||
|
||||
override def renameVars(vals: Map[String, String]): LambdaRaw =
|
||||
IntoIndexRaw(idx.renameVars(vals), `type`)
|
||||
}
|
134
model/raw/src/main/scala/aqua/raw/value/ValueRaw.scala
Normal file
134
model/raw/src/main/scala/aqua/raw/value/ValueRaw.scala
Normal file
@ -0,0 +1,134 @@
|
||||
package aqua.raw.value
|
||||
|
||||
import aqua.types.{BottomType, LiteralType, ScalarType, StreamType, StructType, Type}
|
||||
import cats.data.{Chain, NonEmptyMap}
|
||||
import cats.Eq
|
||||
import scribe.Logging
|
||||
|
||||
sealed trait ValueRaw {
|
||||
|
||||
def usesVarNames: Set[String] = Set.empty
|
||||
|
||||
def resolveWith(map: Map[String, ValueRaw]): ValueRaw = this
|
||||
|
||||
def `type`: Type
|
||||
|
||||
def lastType: Type = `type`
|
||||
|
||||
def renameVars(map: Map[String, String]): ValueRaw = this
|
||||
|
||||
def map(f: ValueRaw => ValueRaw): ValueRaw
|
||||
|
||||
}
|
||||
|
||||
object ValueRaw {
|
||||
|
||||
implicit object ValueRawEq extends Eq[ValueRaw] {
|
||||
override def eqv(x: ValueRaw, y: ValueRaw): Boolean = x == y
|
||||
}
|
||||
|
||||
val InitPeerId: LiteralRaw = LiteralRaw("%init_peer_id%", ScalarType.string)
|
||||
|
||||
val Nil: LiteralRaw = LiteralRaw("[]", StreamType(BottomType))
|
||||
|
||||
val LastError: VarRaw = VarRaw(
|
||||
"%last_error%",
|
||||
StructType(
|
||||
"LastError",
|
||||
NonEmptyMap.of(
|
||||
// These two fields are mandatory for all errors
|
||||
"message" -> ScalarType.string,
|
||||
"error_code" -> ScalarType.i64,
|
||||
// These fields are specific to AquaVM's errors only
|
||||
"instruction" -> ScalarType.string,
|
||||
"peer_id" -> ScalarType.string
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
case class VarRaw(name: String, `type`: Type, lambda: Chain[LambdaRaw] = Chain.empty)
|
||||
extends ValueRaw with Logging {
|
||||
|
||||
override def usesVarNames: Set[String] =
|
||||
lambda.toList.map(_.usesVarNames).foldLeft(Set(name))(_ ++ _)
|
||||
|
||||
override val lastType: Type = lambda.lastOption.map(_.`type`).getOrElse(`type`)
|
||||
|
||||
override def map(f: ValueRaw => ValueRaw): ValueRaw =
|
||||
f(copy(lambda = lambda.map(_.map(f))))
|
||||
|
||||
private def deriveFrom(vm: VarRaw, map: Map[String, ValueRaw]): VarRaw =
|
||||
vm.copy(lambda = vm.lambda.map(_.resolveWith(map)) ++ lambda)
|
||||
|
||||
override def resolveWith(map: Map[String, ValueRaw]): ValueRaw =
|
||||
map.get(name) match {
|
||||
case Some(vv: VarRaw) =>
|
||||
map.get(vv.name) match {
|
||||
case Some(n) =>
|
||||
n match {
|
||||
/* This case protects from infinite recursion
|
||||
when similar names are in a body of a function and a call of a function
|
||||
service Demo("demo"):
|
||||
get4: u64 -> u64
|
||||
|
||||
func two(variable: u64) -> u64:
|
||||
v <- Demo.get4(variable)
|
||||
<- variable
|
||||
|
||||
func three(v: u64) -> u64:
|
||||
variable <- Demo.get4(v)
|
||||
-- here we will try to resolve 'variable' to VarModel('variable')
|
||||
-- that could cause infinite recursion
|
||||
res <- two(variable)
|
||||
<- variable
|
||||
*/
|
||||
case vm @ VarRaw(nn, _, _) if nn == name => deriveFrom(vm, map)
|
||||
// it couldn't go to a cycle as long as the semantics protects it
|
||||
case _ =>
|
||||
n.resolveWith(map) match {
|
||||
case nvm: VarRaw =>
|
||||
deriveFrom(nvm, map)
|
||||
case valueModel =>
|
||||
if (lambda.nonEmpty)
|
||||
logger.error(
|
||||
s"Var $name derived from literal $valueModel, but lambda is lost: $lambda"
|
||||
)
|
||||
valueModel
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
deriveFrom(vv, map)
|
||||
}
|
||||
|
||||
case Some(vv) =>
|
||||
if (lambda.nonEmpty)
|
||||
logger.error(
|
||||
s"Var $name derived from literal $vv, but lambda is lost: $lambda"
|
||||
)
|
||||
vv
|
||||
case None =>
|
||||
this // Should not happen
|
||||
}
|
||||
|
||||
override def renameVars(map: Map[String, String]): ValueRaw =
|
||||
VarRaw(map.getOrElse(name, name), `type`, lambda.map(_.renameVars(map)))
|
||||
|
||||
override def toString: String = s"var{$name: " + `type` + s"}.${lambda.toList.mkString(".")}"
|
||||
}
|
||||
|
||||
case class LiteralRaw(value: String, `type`: Type) extends ValueRaw {
|
||||
override def map(f: ValueRaw => ValueRaw): ValueRaw = f(this)
|
||||
|
||||
override def toString: String = s"{$value: ${`type`}}"
|
||||
}
|
||||
|
||||
object LiteralRaw {
|
||||
def quote(value: String): LiteralRaw = LiteralRaw("\"" + value + "\"", LiteralType.string)
|
||||
|
||||
def number(value: Int): LiteralRaw = LiteralRaw(value.toString, LiteralType.number)
|
||||
|
||||
val True: LiteralRaw = LiteralRaw("true", LiteralType.bool)
|
||||
val False: LiteralRaw = LiteralRaw("false", LiteralType.bool)
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
package aqua.model
|
||||
|
||||
case class ConstantModel(name: String, value: ValueModel, allowOverrides: Boolean) extends Model
|
@ -1,40 +0,0 @@
|
||||
package aqua.model
|
||||
|
||||
import aqua.model.func.raw.FuncOp
|
||||
import cats.kernel.Semigroup
|
||||
|
||||
trait Model
|
||||
|
||||
object Model {
|
||||
def empty(log: String): Model = EmptyModel(log)
|
||||
def error(log: String): Model = EmptyModel(log)
|
||||
|
||||
implicit object MergeModels extends Semigroup[Model] {
|
||||
|
||||
override def combine(x: Model, y: Model): Model = (x, y) match {
|
||||
case (l: FuncOp, r: FuncOp) =>
|
||||
FuncOp.FuncOpSemigroup.combine(l, r)
|
||||
case (l: ScriptModel, r: ScriptModel) =>
|
||||
ScriptModel.SMMonoid.combine(l, r)
|
||||
|
||||
case (l: EmptyModel, r: EmptyModel) => EmptyModel(l.log + " |+| " + r.log)
|
||||
case (_: EmptyModel, r) => r
|
||||
case (l, _: EmptyModel) => l
|
||||
|
||||
case (l, r: ScriptModel) =>
|
||||
ScriptModel.toScriptPart(l).fold(r)(ScriptModel.SMMonoid.combine(_, r))
|
||||
case (l: ScriptModel, r) =>
|
||||
ScriptModel.toScriptPart(r).fold(l)(ScriptModel.SMMonoid.combine(l, _))
|
||||
|
||||
case (l, r) =>
|
||||
ScriptModel
|
||||
.toScriptPart(l)
|
||||
.fold(r)(ls =>
|
||||
ScriptModel.toScriptPart(r).fold(l)(rs => ScriptModel.SMMonoid.combine(ls, rs))
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case class EmptyModel(log: String) extends Model
|
@ -1,30 +0,0 @@
|
||||
package aqua.model
|
||||
|
||||
import aqua.model.func.FuncModel
|
||||
import cats.Monoid
|
||||
import cats.data.Chain
|
||||
|
||||
case class ScriptModel(
|
||||
models: Chain[Model] = Chain.empty
|
||||
) extends Model
|
||||
|
||||
object ScriptModel {
|
||||
|
||||
implicit object SMMonoid extends Monoid[ScriptModel] {
|
||||
override def empty: ScriptModel = ScriptModel()
|
||||
|
||||
override def combine(x: ScriptModel, y: ScriptModel): ScriptModel =
|
||||
ScriptModel(
|
||||
x.models ++ y.models
|
||||
)
|
||||
}
|
||||
|
||||
// Builds a ScriptModel if given model can be considered as a part of a script
|
||||
def toScriptPart(m: Model): Option[ScriptModel] = Option(m).filter {
|
||||
case _: FuncModel => true
|
||||
case _: ServiceModel => true
|
||||
case _: TypeModel => true
|
||||
case _: ConstantModel => true
|
||||
case _ => false
|
||||
}.map(Chain.one).map(ScriptModel(_))
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package aqua.model
|
||||
|
||||
import aqua.types.Type
|
||||
|
||||
case class TypeModel(name: String, `type`: Type) extends Model
|
@ -1,6 +1,7 @@
|
||||
package aqua.model
|
||||
|
||||
import aqua.types._
|
||||
import aqua.raw.value.*
|
||||
import aqua.types.*
|
||||
import cats.Eq
|
||||
import cats.data.{Chain, NonEmptyMap}
|
||||
import scribe.Logging
|
||||
@ -10,7 +11,8 @@ sealed trait ValueModel {
|
||||
|
||||
def lastType: Type
|
||||
|
||||
def resolveWith(map: Map[String, ValueModel]): ValueModel = this
|
||||
// This is a debt: it should never be used
|
||||
def toRaw: ValueRaw
|
||||
}
|
||||
|
||||
object ValueModel {
|
||||
@ -19,107 +21,63 @@ object ValueModel {
|
||||
override def eqv(x: ValueModel, y: ValueModel): Boolean = x == y
|
||||
}
|
||||
|
||||
def varName(vm: ValueModel): Option[String] =
|
||||
vm match {
|
||||
case VarModel(name, _, _) => Some(name)
|
||||
case _ => None
|
||||
}
|
||||
// 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 VarRaw(name, t, lambda) =>
|
||||
VarModel(name, t, lambda.map(LambdaModel.fromRaw))
|
||||
case LiteralRaw(value, t) =>
|
||||
LiteralModel(value, t)
|
||||
}
|
||||
}
|
||||
|
||||
case class LiteralModel(value: String, `type`: Type) extends ValueModel {
|
||||
override def lastType: Type = `type`
|
||||
|
||||
override def toRaw: ValueRaw = LiteralRaw(value, `type`)
|
||||
|
||||
override def toString: String = s"{$value: ${`type`}}"
|
||||
}
|
||||
|
||||
object LiteralModel {
|
||||
def quote(str: String): LiteralModel = LiteralModel("\"" + str + "\"", ScalarType.string)
|
||||
|
||||
val initPeerId: LiteralModel = LiteralModel("%init_peer_id%", ScalarType.string)
|
||||
|
||||
val nil: LiteralModel = LiteralModel(
|
||||
"[]",
|
||||
StreamType(BottomType)
|
||||
)
|
||||
}
|
||||
|
||||
sealed trait LambdaModel {
|
||||
def `type`: Type
|
||||
|
||||
def toRaw: LambdaRaw
|
||||
}
|
||||
case class IntoArrayModel(`type`: Type) extends LambdaModel
|
||||
case class IntoFieldModel(field: String, `type`: Type) extends LambdaModel
|
||||
case class IntoIndexModel(idx: Int, `type`: Type) extends LambdaModel
|
||||
|
||||
case class VarModel(name: String, `type`: Type, lambda: Chain[LambdaModel] = Chain.empty)
|
||||
extends ValueModel with Logging {
|
||||
object LambdaModel {
|
||||
|
||||
def deriveFrom(vm: VarModel): VarModel = vm.copy(lambda = vm.lambda ++ lambda)
|
||||
def fromRaw(l: LambdaRaw): LambdaModel = l match {
|
||||
case IntoFieldRaw(field, t) => IntoFieldModel(field, t)
|
||||
case IntoIndexRaw(idx, t) =>
|
||||
// TODO: handle recursive lambda
|
||||
IntoIndexModel(
|
||||
ValueModel.fromRaw(idx) match {
|
||||
case VarModel(name, _, _) => name
|
||||
case LiteralModel(value, _) => value
|
||||
},
|
||||
t
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
case class IntoFieldModel(field: String, `type`: Type) extends LambdaModel {
|
||||
override def toRaw: LambdaRaw = IntoFieldRaw(field, `type`)
|
||||
}
|
||||
|
||||
case class IntoIndexModel(idx: String, `type`: Type) extends LambdaModel {
|
||||
|
||||
override def toRaw: LambdaRaw = IntoIndexRaw(
|
||||
if (idx.forall(Character.isDigit)) LiteralRaw.number(idx.toInt)
|
||||
else VarRaw(idx, ScalarType.string),
|
||||
`type`
|
||||
)
|
||||
}
|
||||
|
||||
case class VarModel(name: String, `type`: Type, lambda: Chain[LambdaModel]) extends ValueModel {
|
||||
|
||||
override val lastType: Type = lambda.lastOption.map(_.`type`).getOrElse(`type`)
|
||||
|
||||
override def resolveWith(map: Map[String, ValueModel]): ValueModel =
|
||||
map.get(name) match {
|
||||
case Some(vv: VarModel) =>
|
||||
map.get(vv.name) match {
|
||||
case Some(n) =>
|
||||
n match {
|
||||
/* This case protects from infinite recursion
|
||||
when similar names are in a body of a function and a call of a function
|
||||
service Demo("demo"):
|
||||
get4: u64 -> u64
|
||||
override def toRaw: ValueRaw = VarRaw(name, `type`, lambda.map(_.toRaw))
|
||||
|
||||
func two(variable: u64) -> u64:
|
||||
v <- Demo.get4(variable)
|
||||
<- variable
|
||||
|
||||
func three(v: u64) -> u64:
|
||||
variable <- Demo.get4(v)
|
||||
-- here we will try to resolve 'variable' to VarModel('variable')
|
||||
-- that could cause infinite recursion
|
||||
res <- two(variable)
|
||||
<- variable
|
||||
*/
|
||||
case vm @ VarModel(nn, _, _) if nn == name => deriveFrom(vm)
|
||||
// it couldn't go to a cycle as long as the semantics protects it
|
||||
case _ =>
|
||||
n.resolveWith(map) match {
|
||||
case nvm: VarModel =>
|
||||
deriveFrom(nvm)
|
||||
case valueModel =>
|
||||
if (lambda.nonEmpty)
|
||||
logger.error(
|
||||
s"Var $name derived from scalar $valueModel, but lambda is lost: $lambda"
|
||||
)
|
||||
valueModel
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
deriveFrom(vv)
|
||||
}
|
||||
|
||||
case Some(vv) =>
|
||||
vv // TODO check that lambda is empty, otherwise error
|
||||
case None =>
|
||||
this // Should not happen
|
||||
}
|
||||
|
||||
override def toString(): String = s"var{$name: " + `type` + s"}.${lambda.toList.mkString(".")}"
|
||||
}
|
||||
|
||||
object VarModel {
|
||||
|
||||
val lastError: VarModel = VarModel(
|
||||
"%last_error%",
|
||||
StructType(
|
||||
"LastError",
|
||||
NonEmptyMap.of(
|
||||
// These two fields are mandatory for all errors
|
||||
"message" -> ScalarType.string,
|
||||
"error_code" -> ScalarType.i64,
|
||||
// These fields are specific to AquaVM's errors only
|
||||
"instruction" -> ScalarType.string,
|
||||
"peer_id" -> ScalarType.string
|
||||
)
|
||||
)
|
||||
)
|
||||
override def toString: String = s"var{$name: " + `type` + s"}.${lambda.toList.mkString(".")}"
|
||||
}
|
||||
|
@ -1,11 +0,0 @@
|
||||
package aqua.model.func
|
||||
|
||||
import aqua.model.func.raw.FuncOp
|
||||
import aqua.types.ArrowType
|
||||
import aqua.model.{Model, ValueModel}
|
||||
|
||||
case class ArrowModel(
|
||||
`type`: ArrowType,
|
||||
ret: List[ValueModel],
|
||||
body: FuncOp
|
||||
) extends Model
|
@ -1,33 +0,0 @@
|
||||
package aqua.model.func
|
||||
|
||||
import aqua.model.{ValueModel, VarModel}
|
||||
import aqua.types.Type
|
||||
|
||||
// TODO docs
|
||||
case class Call(args: List[ValueModel], exportTo: List[Call.Export]) {
|
||||
|
||||
def mapValues(f: ValueModel => ValueModel): Call =
|
||||
Call(
|
||||
args.map(f),
|
||||
exportTo
|
||||
)
|
||||
|
||||
// TODO docs
|
||||
def mapExport(f: String => String): Call = copy(exportTo = exportTo.map(_.mapName(f)))
|
||||
|
||||
def argVarNames: Set[String] = args.collect { case VarModel(name, _, _) =>
|
||||
name
|
||||
}.toSet
|
||||
|
||||
override def toString: String =
|
||||
s"[${args.mkString(" ")}]${exportTo.map(_.model).map(" " + _).mkString(",")}"
|
||||
}
|
||||
|
||||
object Call {
|
||||
// TODO docs
|
||||
case class Export(name: String, `type`: Type) {
|
||||
def mapName(f: String => String): Export = copy(f(name))
|
||||
|
||||
def model: ValueModel = VarModel(name, `type`)
|
||||
}
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
package aqua.model.func
|
||||
|
||||
import aqua.model.{ValueModel, VarModel}
|
||||
import aqua.model.func.raw.{AssignmentTag, CallArrowTag, ClosureTag, FuncOp, RawTag}
|
||||
import aqua.types.ArrowType
|
||||
import scribe.Logging
|
||||
|
||||
case class FuncApplyAcc(
|
||||
noNames: Set[String],
|
||||
resolvedExports: Map[String, ValueModel],
|
||||
resolvedArrows: Map[String, FuncCallable],
|
||||
funcName: String,
|
||||
instructionCounter: Int = 0
|
||||
) extends Logging {
|
||||
|
||||
// resolve values of this tag with resolved exports
|
||||
def resolve(tag: RawTag): RawTag = tag.mapValues(_.resolveWith(resolvedExports))
|
||||
|
||||
// resolve values of this tag with resolved exports, lift to Cofree as a leaf
|
||||
def resolveLeaf(tag: RawTag): FuncOp.Tree =
|
||||
FuncOp.leaf(tag.mapValues(_.resolveWith(resolvedExports))).tree
|
||||
|
||||
def incr: FuncApplyAcc = copy(instructionCounter = instructionCounter + 1)
|
||||
|
||||
// Register the new export
|
||||
def withResolvedExport(exportName: String, value: ValueModel): FuncApplyAcc =
|
||||
incr.copy(resolvedExports =
|
||||
resolvedExports + (exportName -> value.resolveWith(resolvedExports))
|
||||
)
|
||||
|
||||
// Register the new arrow
|
||||
def withResolvedArrow(arrow: FuncModel): FuncApplyAcc =
|
||||
incr.copy(resolvedArrows =
|
||||
resolvedArrows + (arrow.name -> arrow.capture(resolvedArrows, resolvedExports))
|
||||
)
|
||||
|
||||
// Arrow call: resolve, register exports
|
||||
def callArrow(name: String, call: Call): (FuncApplyAcc, FuncOp.Tree) = {
|
||||
// Apply arguments to a function – recursion
|
||||
val callResolved = call.mapValues(_.resolveWith(resolvedExports))
|
||||
val possibleArrowNames = callResolved.args.collect { case VarModel(m, _: ArrowType, _) =>
|
||||
m
|
||||
}.toSet
|
||||
|
||||
val (appliedOp, value) =
|
||||
resolvedArrows(name)
|
||||
.resolve(
|
||||
callResolved,
|
||||
resolvedArrows.view.filterKeys(possibleArrowNames).toMap,
|
||||
noNames
|
||||
)
|
||||
.value
|
||||
|
||||
// Function defines new names inside its body – need to collect them
|
||||
// TODO: actually it's done and dropped – so keep and pass it instead
|
||||
val newNames = appliedOp.definesVarNames.value
|
||||
// At the very end, will need to resolve what is used as results with the result values
|
||||
incr.copy(
|
||||
noNames ++ newNames,
|
||||
resolvedExports ++ call.exportTo.map(_.name).zip(value)
|
||||
) -> appliedOp.tree
|
||||
}
|
||||
|
||||
def handleTag(tag: RawTag): (FuncApplyAcc, FuncOp.Tree) =
|
||||
tag match {
|
||||
case CallArrowTag(fn, c) if resolvedArrows.contains(fn) =>
|
||||
callArrow(fn, c)
|
||||
case tag @ ClosureTag(arrow) =>
|
||||
withResolvedArrow(arrow) -> resolveLeaf(tag)
|
||||
case tag @ AssignmentTag(value, assignTo) =>
|
||||
withResolvedExport(assignTo, value) -> resolveLeaf(tag)
|
||||
case CallArrowTag(fn, _) =>
|
||||
logger.error(
|
||||
s"UNRESOLVED arrow $fn in $funcName, skipping, will become (null) in AIR!"
|
||||
)
|
||||
this -> resolveLeaf(tag)
|
||||
case _ =>
|
||||
// All the other tags are already resolved and need no substitution
|
||||
this -> resolveLeaf(tag)
|
||||
}
|
||||
|
||||
}
|
@ -1,143 +0,0 @@
|
||||
package aqua.model.func
|
||||
|
||||
import aqua.model.ValueModel.varName
|
||||
import aqua.model.func.raw.*
|
||||
import aqua.model.{Model, ValueModel, VarModel}
|
||||
import aqua.types.*
|
||||
import cats.Eval
|
||||
import cats.data.Chain
|
||||
import cats.free.Cofree
|
||||
import scribe.Logging
|
||||
|
||||
// TODO docs for class and all args
|
||||
case class FuncCallable(
|
||||
funcName: String,
|
||||
body: FuncOp,
|
||||
arrowType: ArrowType,
|
||||
ret: List[ValueModel],
|
||||
capturedArrows: Map[String, FuncCallable],
|
||||
capturedValues: Map[String, ValueModel]
|
||||
) extends Model with Logging {
|
||||
|
||||
lazy val args: List[(String, Type)] = arrowType.domain.toLabelledList()
|
||||
lazy val argNames: List[String] = args.map(_._1)
|
||||
|
||||
def findNewNames(forbidden: Set[String], introduce: Set[String]): Map[String, String] =
|
||||
(forbidden intersect introduce).foldLeft(Map.empty[String, String]) { case (acc, name) =>
|
||||
acc + (name -> LazyList
|
||||
.from(0)
|
||||
.map(name + _)
|
||||
.dropWhile(n => forbidden(n) || introduce(n) || acc.contains(n))
|
||||
.head)
|
||||
}
|
||||
|
||||
def isStream(vm: ValueModel): Boolean =
|
||||
vm.`type` match {
|
||||
case StreamType(_) => true
|
||||
case _ => false
|
||||
}
|
||||
|
||||
def extractStreamArgs(args: Map[String, ValueModel]): Map[String, ValueModel] =
|
||||
args.filter(arg => isStream(arg._2))
|
||||
|
||||
// Apply a callable function, get its fully resolved body & optional value, if any
|
||||
def resolve(
|
||||
call: Call,
|
||||
arrows: Map[String, FuncCallable],
|
||||
forbiddenNames: Set[String]
|
||||
): Eval[(FuncOp, List[ValueModel])] = {
|
||||
|
||||
logger.debug("Call: " + call)
|
||||
|
||||
// Collect all arguments: what names are used inside the function, what values are received
|
||||
val argsFull = ArgsCall(arrowType.domain, call.args)
|
||||
// DataType arguments
|
||||
val argsToDataRaw = argsFull.dataArgs
|
||||
// Arrow arguments: expected type is Arrow, given by-name
|
||||
val argsToArrowsRaw = argsFull.arrowArgs(arrows)
|
||||
|
||||
// collect arguments with stream type
|
||||
// to exclude it from resolving and rename it with a higher-level stream that passed by argument
|
||||
val streamArgs = extractStreamArgs(argsToDataRaw)
|
||||
val streamToRename = streamArgs.map { case (k, v) => (k, varName(v)) }.collect {
|
||||
case (k, Some(v)) => (k, v)
|
||||
}
|
||||
|
||||
// Find all duplicates in arguments
|
||||
val argsShouldRename = findNewNames(forbiddenNames, (argsToDataRaw ++ argsToArrowsRaw).keySet)
|
||||
// we shoudln't rename arguments that will be renamed by 'streamToRename'
|
||||
.filter { case (k, _) => !streamToRename.contains(k) }
|
||||
val argsToData = argsToDataRaw.map { case (k, v) => argsShouldRename.getOrElse(k, k) -> v }
|
||||
val argsToArrows = argsToArrowsRaw.map { case (k, v) => argsShouldRename.getOrElse(k, k) -> v }
|
||||
|
||||
// Going to resolve arrows: collect them all. Names should never collide: it's semantically checked
|
||||
val allArrows = capturedArrows ++ argsToArrows
|
||||
|
||||
// Substitute arguments (referenced by name and optional lambda expressions) with values
|
||||
// Also rename all renamed arguments in the body
|
||||
val treeWithValues =
|
||||
body.rename(argsShouldRename).resolveValues(argsToData).rename(streamToRename)
|
||||
|
||||
// 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 -- streamArgs.values.collect {
|
||||
case VarModel(streamNameWasAlreadySubstitutedAbove, _, _) =>
|
||||
streamNameWasAlreadySubstitutedAbove
|
||||
} -- call.exportTo.filter { exp =>
|
||||
exp.`type` match {
|
||||
case StreamType(_) => false
|
||||
case _ => true
|
||||
}
|
||||
}.map(_.name)
|
||||
|
||||
// We have some names in scope (forbiddenNames), can't introduce them again; so find new names
|
||||
val shouldRename = findNewNames(forbiddenNames, treeDefines)
|
||||
|
||||
// If there was a collision, rename exports and usages with new names
|
||||
val treeRenamed = treeWithValues.rename(shouldRename)
|
||||
|
||||
// Result could be derived from arguments, or renamed; take care about that
|
||||
val result: List[ValueModel] = ret.map(_.resolveWith(argsToData)).map {
|
||||
case v: VarModel if shouldRename.contains(v.name) => v.copy(shouldRename(v.name))
|
||||
case v => v
|
||||
}
|
||||
|
||||
// Now, substitute the arrows that were received as function arguments
|
||||
FuncOp
|
||||
.traverseA(
|
||||
// Use the new op tree (args are replaced with values, names are unique & safe)
|
||||
treeRenamed.tree,
|
||||
// Accumulator: all used names are forbidden, if we set any more names -- forbid them as well
|
||||
FuncApplyAcc(
|
||||
forbiddenNames ++ shouldRename.values ++ treeDefines,
|
||||
// Functions may export variables, so collect them
|
||||
capturedValues,
|
||||
// They also can define arrows!
|
||||
allArrows,
|
||||
// Function name we're handling...
|
||||
funcName
|
||||
)
|
||||
)(_.handleTag(_))
|
||||
.map { case (acc, callableFuncBody) =>
|
||||
// If return value is affected by any of internal functions, resolve it
|
||||
val resolvedResult = result.map(_.resolveWith(acc.resolvedExports))
|
||||
|
||||
val (ops, rets) = (call.exportTo zip resolvedResult)
|
||||
.map[(Option[FuncOp], ValueModel)] {
|
||||
case (exp @ Call.Export(_, StreamType(_)), res) =>
|
||||
// pass nested function results to a stream
|
||||
Some(FuncOps.pushToStream(res, exp)) -> exp.model
|
||||
case (_, res) =>
|
||||
None -> res
|
||||
}
|
||||
.foldLeft[(List[FuncOp], List[ValueModel])]((FuncOp(callableFuncBody) :: Nil, Nil)) {
|
||||
case ((ops, rets), (Some(fo), r)) => (fo :: ops, r :: rets)
|
||||
case ((ops, rets), (_, r)) => (ops, r :: rets)
|
||||
}
|
||||
|
||||
FuncOps.seq(ops.reverse: _*) -> rets.reverse
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
package aqua.model.func
|
||||
|
||||
import aqua.model.func.raw.FuncOp
|
||||
import aqua.model.{Model, ValueModel}
|
||||
import aqua.types.ArrowType
|
||||
|
||||
case class FuncModel(
|
||||
name: String,
|
||||
arrow: ArrowModel
|
||||
) extends Model {
|
||||
|
||||
def capture(
|
||||
arrows: Map[String, FuncCallable],
|
||||
constants: Map[String, ValueModel]
|
||||
): FuncCallable =
|
||||
FuncCallable(name, arrow.body.fixXorPar, arrow.`type`, arrow.ret, arrows, constants)
|
||||
|
||||
}
|
@ -1,175 +0,0 @@
|
||||
package aqua.model.func.raw
|
||||
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.{Model, ValueModel, VarModel}
|
||||
import cats.Eval
|
||||
import cats.data.Chain
|
||||
import cats.free.Cofree
|
||||
import cats.instances.tuple.*
|
||||
import cats.kernel.Semigroup
|
||||
import cats.syntax.apply.*
|
||||
import cats.syntax.functor.*
|
||||
|
||||
case class FuncOp(tree: Cofree[Chain, RawTag]) extends Model {
|
||||
def head: RawTag = tree.head
|
||||
|
||||
lazy val isRightAssoc: Boolean = head match {
|
||||
case XorTag | ParTag => true
|
||||
case _: XorParTag => true
|
||||
case _ => false
|
||||
}
|
||||
|
||||
def cata[T](folder: (RawTag, Chain[T]) => Eval[T]): Eval[T] =
|
||||
Cofree.cata(tree)(folder)
|
||||
|
||||
def definesVarNames: Eval[Set[String]] = cata[Set[String]] {
|
||||
case (CallArrowTag(_, Call(_, exportTo)), acc) if exportTo.nonEmpty =>
|
||||
Eval.later(acc.foldLeft(exportTo.map(_.name).toSet)(_ ++ _))
|
||||
case (PushToStreamTag(_, exportTo), acc) =>
|
||||
Eval.later(acc.foldLeft(Set(exportTo.name))(_ ++ _))
|
||||
case (CanonicalizeTag(_, exportTo), acc) =>
|
||||
Eval.later(acc.foldLeft(Set(exportTo.name))(_ ++ _))
|
||||
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])(_ ++ _))
|
||||
}
|
||||
|
||||
def exportsVarNames: Eval[Set[String]] = cata[Set[String]] {
|
||||
case (CallArrowTag(_, Call(_, exportTo)), acc) if exportTo.nonEmpty =>
|
||||
Eval.later(acc.foldLeft(exportTo.map(_.name).toSet)(_ ++ _))
|
||||
case (CallServiceTag(_, _, Call(_, exportTo)), acc) if exportTo.nonEmpty =>
|
||||
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])(_ ++ _))
|
||||
}
|
||||
|
||||
// TODO: as it is used for checking of intersection, make it a lazy traverse with fail-fast
|
||||
def usesVarNames: Eval[Set[String]] = cata[Set[String]] {
|
||||
case (CallArrowTag(_, call), acc) =>
|
||||
Eval.later(acc.foldLeft(call.argVarNames)(_ ++ _))
|
||||
case (CallServiceTag(_, _, call), acc) =>
|
||||
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) =>
|
||||
Eval.later(acc.foldLeft(ValueModel.varName(a).toSet ++ ValueModel.varName(b))(_ ++ _))
|
||||
case (ForTag(_, VarModel(name, _, _)), acc) =>
|
||||
Eval.later(acc.foldLeft(Set(name))(_ ++ _))
|
||||
case (_, acc) => Eval.later(acc.foldLeft(Set.empty[String])(_ ++ _))
|
||||
}
|
||||
|
||||
def resolveValues(vals: Map[String, ValueModel]): FuncOp =
|
||||
FuncOp(tree.map[RawTag](_.mapValues(_.resolveWith(vals))))
|
||||
|
||||
def rename(vals: Map[String, String]): FuncOp = {
|
||||
if (vals.isEmpty)
|
||||
this
|
||||
else
|
||||
FuncOp(
|
||||
tree.map[RawTag](op =>
|
||||
op.mapValues {
|
||||
case v: VarModel if vals.contains(v.name) => v.copy(name = vals(v.name))
|
||||
case v => v
|
||||
} match {
|
||||
case c: CallArrowTag => c.copy(call = c.call.mapExport(n => vals.getOrElse(n, n)))
|
||||
case c: CallServiceTag => c.copy(call = c.call.mapExport(n => vals.getOrElse(n, n)))
|
||||
case a: PushToStreamTag =>
|
||||
a.copy(exportTo = a.exportTo.mapName(n => vals.getOrElse(n, n)))
|
||||
case a: CanonicalizeTag =>
|
||||
a.copy(exportTo = a.exportTo.mapName(n => vals.getOrElse(n, n)))
|
||||
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
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
def :+:(prev: FuncOp): FuncOp =
|
||||
FuncOp.RightAssocSemi.combine(prev, this)
|
||||
|
||||
// Function body must be fixed before function gets resolved
|
||||
def fixXorPar: FuncOp =
|
||||
FuncOp(cata[Cofree[Chain, RawTag]] {
|
||||
case (XorParTag(left, right), _) =>
|
||||
Eval.now(
|
||||
FuncOps
|
||||
.par(
|
||||
FuncOp.wrap(XorTag, left),
|
||||
right
|
||||
)
|
||||
.tree
|
||||
)
|
||||
|
||||
case (head, tail) => Eval.now(Cofree(head, Eval.now(tail)))
|
||||
}.value)
|
||||
}
|
||||
|
||||
object FuncOp {
|
||||
type Tree = Cofree[Chain, RawTag]
|
||||
|
||||
def traverseA[A](cf: Tree, init: A)(
|
||||
f: (A, RawTag) => (A, Tree)
|
||||
): Eval[(A, Tree)] = {
|
||||
val (headA, head) = f(init, cf.head)
|
||||
// TODO: it should be in standard library, with some other types
|
||||
cf.tail
|
||||
.map(_.foldLeft[(A, Chain[Tree])]((headA, head.tailForced)) {
|
||||
case ((aggrA, aggrTail), child) =>
|
||||
traverseA(child, aggrA)(f).value match { case (a, tree) => (a, aggrTail.append(tree)) }
|
||||
})
|
||||
.map { case (a, ch) => (a, head.copy(tail = Eval.now(ch))) }
|
||||
}
|
||||
|
||||
// Semigroup for foldRight processing
|
||||
object RightAssocSemi extends Semigroup[FuncOp] {
|
||||
|
||||
override def combine(x: FuncOp, y: FuncOp): FuncOp = (x.tree.head, y.tree.head) match {
|
||||
case (_: ParGroupTag, ParTag) =>
|
||||
FuncOp(y.tree.copy(tail = (x.tree.tail, y.tree.tail).mapN(_ ++ _)))
|
||||
case (XorTag, XorTag) =>
|
||||
FuncOp(y.tree.copy(tail = (x.tree.tail, y.tree.tail).mapN(_ ++ _)))
|
||||
case (XorTag.LeftBiased, XorTag) =>
|
||||
wrap(SeqTag, FuncOp(y.tree.copy(tail = (x.tree.tail, y.tree.tail).mapN(_ ++ _))))
|
||||
case (XorTag, ParTag) => FuncOp(Cofree[Chain, RawTag](XorParTag(x, y), Eval.now(Chain.empty)))
|
||||
case (_, ParTag | XorTag) =>
|
||||
wrap(SeqTag, FuncOp(y.tree.copy(tail = y.tree.tail.map(_.prepend(x.tree)))))
|
||||
case (_, XorParTag(xor, par)) =>
|
||||
combine(combine(x, xor), par)
|
||||
case _ => FuncOpSemigroup.combine(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
implicit object FuncOpSemigroup extends Semigroup[FuncOp] {
|
||||
|
||||
override def combine(x: FuncOp, y: FuncOp): FuncOp = (x.tree.head, y.tree.head) match {
|
||||
case (_, XorParTag(xor, par)) => combine(combine(x, xor), par)
|
||||
case (XorParTag(xor, par), _) => combine(combine(xor, par), y)
|
||||
case (SeqTag, SeqTag) => FuncOp(y.tree.copy(tail = (x.tree.tail, y.tree.tail).mapN(_ ++ _)))
|
||||
case (_, SeqTag) => FuncOp(y.tree.copy(tail = y.tree.tail.map(_.prepend(x.tree))))
|
||||
case (SeqTag, _) => FuncOp(x.tree.copy(tail = x.tree.tail.map(_.append(y.tree))))
|
||||
case _ => node(SeqTag, Chain(x, y))
|
||||
}
|
||||
}
|
||||
|
||||
def leaf(tag: RawTag): FuncOp = FuncOp(Cofree[Chain, RawTag](tag, Eval.now(Chain.empty)))
|
||||
|
||||
def wrap(tag: RawTag, child: FuncOp): FuncOp = node(tag, Chain.one(child))
|
||||
|
||||
def node(tag: RawTag, children: Chain[FuncOp]): FuncOp =
|
||||
FuncOp(Cofree[Chain, RawTag](tag, Eval.later(children.map(_.tree))))
|
||||
}
|
@ -1,171 +0,0 @@
|
||||
package aqua.model.func.raw
|
||||
|
||||
import aqua.model.ValueModel
|
||||
import aqua.model.ValueModel.varName
|
||||
import aqua.model.func.{Call, FuncModel}
|
||||
import cats.data.NonEmptyList
|
||||
import cats.data.Chain
|
||||
|
||||
sealed trait RawTag {
|
||||
// What variable names this tag uses (children are not respected)
|
||||
def usesVarNames: Set[String] = Set.empty
|
||||
|
||||
def mapValues(f: ValueModel => ValueModel): RawTag = this match {
|
||||
case OnTag(peerId, via) => OnTag(f(peerId), via.map(f))
|
||||
case MatchMismatchTag(left, right, shouldMatch) =>
|
||||
MatchMismatchTag(f(left), f(right), shouldMatch)
|
||||
case ForTag(item, iterable) => ForTag(item, f(iterable))
|
||||
case CallArrowTag(funcName, call) =>
|
||||
CallArrowTag(
|
||||
funcName,
|
||||
call.mapValues(f)
|
||||
)
|
||||
case CallServiceTag(serviceId, funcName, call) =>
|
||||
CallServiceTag(
|
||||
f(serviceId),
|
||||
funcName,
|
||||
call.mapValues(f)
|
||||
)
|
||||
case PushToStreamTag(operand, exportTo) =>
|
||||
PushToStreamTag(
|
||||
f(operand),
|
||||
exportTo
|
||||
)
|
||||
case CanonicalizeTag(operand, exportTo) =>
|
||||
CanonicalizeTag(
|
||||
f(operand),
|
||||
exportTo
|
||||
)
|
||||
case AssignmentTag(value, assignTo) =>
|
||||
AssignmentTag(f(value), assignTo)
|
||||
case ReturnTag(values) =>
|
||||
ReturnTag(values.map(f))
|
||||
case DeclareStreamTag(value) =>
|
||||
DeclareStreamTag(f(value))
|
||||
case AbilityIdTag(value, ability) =>
|
||||
AbilityIdTag(f(value), ability)
|
||||
case ClosureTag(func) =>
|
||||
ClosureTag(
|
||||
func.copy(arrow =
|
||||
func.arrow.copy(
|
||||
ret = func.arrow.ret.map(f),
|
||||
body = FuncOp(func.arrow.body.tree.map(_.mapValues(f)))
|
||||
)
|
||||
)
|
||||
)
|
||||
case _ => this
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sealed trait NoExecTag extends RawTag
|
||||
|
||||
sealed trait GroupTag extends RawTag
|
||||
|
||||
sealed trait SeqGroupTag extends GroupTag
|
||||
|
||||
sealed trait ParGroupTag extends GroupTag
|
||||
|
||||
case object SeqTag extends SeqGroupTag
|
||||
|
||||
case object ParTag extends ParGroupTag {
|
||||
case object Detach extends ParGroupTag
|
||||
}
|
||||
|
||||
case object XorTag extends GroupTag {
|
||||
case object LeftBiased extends GroupTag
|
||||
}
|
||||
|
||||
case class XorParTag(xor: FuncOp, par: FuncOp) extends RawTag {
|
||||
// Collect all the used variable names
|
||||
override def usesVarNames: Set[String] = xor.usesVarNames.value ++ par.usesVarNames.value
|
||||
}
|
||||
|
||||
case class OnTag(peerId: ValueModel, via: Chain[ValueModel]) extends SeqGroupTag {
|
||||
|
||||
override def usesVarNames: Set[String] =
|
||||
ValueModel.varName(peerId).toSet ++ via.iterator.flatMap(ValueModel.varName)
|
||||
|
||||
override def toString: String =
|
||||
s"(on $peerId${if (via.nonEmpty) " via " + via.toList.mkString(" via ") else ""})"
|
||||
}
|
||||
|
||||
case class NextTag(item: String) extends RawTag {
|
||||
override def usesVarNames: Set[String] = Set(item)
|
||||
}
|
||||
|
||||
case class RestrictionTag(name: String, isStream: Boolean) extends SeqGroupTag {
|
||||
override def usesVarNames: Set[String] = Set(name)
|
||||
}
|
||||
|
||||
case class MatchMismatchTag(left: ValueModel, right: ValueModel, shouldMatch: Boolean)
|
||||
extends SeqGroupTag {
|
||||
|
||||
override def usesVarNames: Set[String] =
|
||||
ValueModel.varName(left).toSet ++ ValueModel.varName(right).toSet
|
||||
}
|
||||
|
||||
case class ForTag(item: String, iterable: ValueModel) extends SeqGroupTag {
|
||||
override def usesVarNames: Set[String] = Set(item) ++ ValueModel.varName(iterable)
|
||||
}
|
||||
|
||||
case class CallArrowTag(
|
||||
funcName: String,
|
||||
call: Call
|
||||
) extends RawTag {
|
||||
override def usesVarNames: Set[String] = call.argVarNames
|
||||
}
|
||||
|
||||
case class DeclareStreamTag(
|
||||
value: ValueModel
|
||||
) extends NoExecTag {
|
||||
override def usesVarNames: Set[String] = ValueModel.varName(value).toSet
|
||||
}
|
||||
|
||||
case class AssignmentTag(
|
||||
value: ValueModel,
|
||||
assignTo: String
|
||||
) extends NoExecTag {
|
||||
override def usesVarNames: Set[String] = Set(assignTo) ++ ValueModel.varName(value)
|
||||
}
|
||||
|
||||
case class ClosureTag(
|
||||
func: FuncModel
|
||||
) extends NoExecTag {
|
||||
// TODO captured names are lost?
|
||||
override def usesVarNames: Set[String] = Set(func.name)
|
||||
}
|
||||
|
||||
case class ReturnTag(
|
||||
values: NonEmptyList[ValueModel]
|
||||
) extends NoExecTag
|
||||
|
||||
object EmptyTag extends NoExecTag
|
||||
|
||||
case class AbilityIdTag(
|
||||
value: ValueModel,
|
||||
service: String
|
||||
) extends NoExecTag
|
||||
|
||||
case class CallServiceTag(
|
||||
serviceId: ValueModel,
|
||||
funcName: String,
|
||||
call: Call
|
||||
) extends RawTag {
|
||||
|
||||
override def usesVarNames: Set[String] = ValueModel.varName(serviceId).toSet ++ call.argVarNames
|
||||
|
||||
override def toString: String = s"(call _ ($serviceId $funcName) $call)"
|
||||
}
|
||||
|
||||
case class PushToStreamTag(operand: ValueModel, exportTo: Call.Export) extends RawTag {
|
||||
override def usesVarNames: Set[String] = ValueModel.varName(operand).toSet
|
||||
|
||||
override def toString: String = s"(push $operand $exportTo)"
|
||||
}
|
||||
|
||||
case class CanonicalizeTag(operand: ValueModel, exportTo: Call.Export) extends RawTag {
|
||||
override def usesVarNames: Set[String] = ValueModel.varName(operand).toSet
|
||||
|
||||
override def toString: String = s"(can $operand $exportTo)"
|
||||
}
|
216
model/src/main/scala/aqua/model/inline/ArrowInliner.scala
Normal file
216
model/src/main/scala/aqua/model/inline/ArrowInliner.scala
Normal file
@ -0,0 +1,216 @@
|
||||
package aqua.model.inline
|
||||
|
||||
import aqua.model.ValueModel
|
||||
import aqua.model.inline.state.{Arrows, Counter, Exports, Mangler}
|
||||
import aqua.raw.arrow.{ArgsCall, FuncArrow}
|
||||
import cats.Eval
|
||||
import scribe.{log, Logging}
|
||||
import aqua.raw.ops.{AssignmentTag, Call, CallArrowTag, ClosureTag, FuncOp, FuncOps, RawTag, SeqTag}
|
||||
import aqua.raw.value.{ValueRaw, VarRaw}
|
||||
import aqua.types.*
|
||||
import cats.Eval
|
||||
import cats.data.Chain
|
||||
import cats.free.Cofree
|
||||
import cats.data.State
|
||||
|
||||
object ArrowInliner extends Logging {
|
||||
|
||||
// TODO: return ValueModel – values are substituted and resolved on this stage
|
||||
// TODO: FuncOp is also not complete: it still has topology, but not arrow calls; how to show it? ResTop?
|
||||
// Apply a callable function, get its fully resolved body & optional value, if any
|
||||
def inline[S: Mangler: Arrows: Exports: Counter](
|
||||
fn: FuncArrow,
|
||||
call: Call
|
||||
): State[S, (FuncOp, List[ValueRaw])] =
|
||||
// Function's internal variables will not be available outside, hence the scope
|
||||
Exports[S].scope(
|
||||
for {
|
||||
// Process renamings, prepare environment
|
||||
tr <- prelude[S](fn, call)
|
||||
(tree, result) = tr
|
||||
|
||||
// Register captured values as available exports
|
||||
_ <- Exports[S].resolved(fn.capturedValues)
|
||||
|
||||
// Now, substitute the arrows that were received as function arguments
|
||||
// Use the new op tree (args are replaced with values, names are unique & safe)
|
||||
callableFuncBody <- handleTree(tree)
|
||||
|
||||
// Fix return values with exports collected in the body
|
||||
resolvedExports <- Exports[S].exports
|
||||
resolvedResult = result.map(_.resolveWith(resolvedExports))
|
||||
|
||||
// Fix the return values
|
||||
(ops, rets) = (call.exportTo zip resolvedResult)
|
||||
.map[(Option[FuncOp], ValueRaw)] {
|
||||
case (exp @ Call.Export(_, StreamType(_)), res) =>
|
||||
// pass nested function results to a stream
|
||||
Some(FuncOps.pushToStream(res, exp)) -> exp.toRaw
|
||||
case (_, res) =>
|
||||
None -> res
|
||||
}
|
||||
.foldLeft[(List[FuncOp], List[ValueRaw])]((FuncOp(callableFuncBody) :: Nil, Nil)) {
|
||||
case ((ops, rets), (Some(fo), r)) => (fo :: ops, r :: rets)
|
||||
case ((ops, rets), (_, r)) => (ops, r :: rets)
|
||||
}
|
||||
} yield FuncOps.seq(ops.reverse: _*) -> rets.reverse
|
||||
)
|
||||
|
||||
/**
|
||||
* Prepare the state context for this function call
|
||||
*
|
||||
* @param fn
|
||||
* Function that will be called
|
||||
* @param call
|
||||
* Call object
|
||||
* @tparam S
|
||||
* State
|
||||
* @return
|
||||
* Tree with substituted values, list of return values prior to function calling/inlining
|
||||
*/
|
||||
private def prelude[S: Mangler: Arrows](
|
||||
fn: FuncArrow,
|
||||
call: Call
|
||||
): State[S, (FuncOp.Tree, List[ValueRaw])] =
|
||||
for {
|
||||
// Collect all arguments: what names are used inside the function, what values are received
|
||||
argsFull <- State.pure(ArgsCall(fn.arrowType.domain, call.args))
|
||||
|
||||
// DataType arguments
|
||||
argsToDataRaw = argsFull.dataArgs
|
||||
|
||||
// Arrow arguments: expected type is Arrow, given by-name
|
||||
argsToArrowsRaw <- Arrows[S].argsArrows(argsFull)
|
||||
|
||||
// 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???
|
||||
streamToRename = argsFull.streamArgs.view.mapValues(_.name).toMap
|
||||
|
||||
// Find all duplicates in arguments
|
||||
// we should not rename arguments that will be renamed by 'streamToRename'
|
||||
argsShouldRename <- Mangler[S].findNewNames(
|
||||
argsToDataRaw.keySet ++ argsToArrowsRaw.keySet -- streamToRename.keySet
|
||||
)
|
||||
|
||||
argsToData = argsToDataRaw.map { case (k, v) => argsShouldRename.getOrElse(k, k) -> v }
|
||||
argsToArrows = argsToArrowsRaw.map { case (k, v) => argsShouldRename.getOrElse(k, k) -> v }
|
||||
|
||||
// Going to resolve arrows: collect them all. Names should never collide: it's semantically checked
|
||||
_ <- Arrows[S].purge
|
||||
_ <- Arrows[S].resolved(fn.capturedArrows ++ argsToArrows)
|
||||
|
||||
// Substitute arguments (referenced by name and optional lambda expressions) with values
|
||||
// Also rename all renamed arguments in the body
|
||||
treeWithValues =
|
||||
fn.body
|
||||
.rename(argsShouldRename)
|
||||
.resolveValues(argsToData)
|
||||
.rename(streamToRename)
|
||||
|
||||
// Function body on its own defines some values; collect their names
|
||||
// except stream arguments. They should be already renamed
|
||||
treeDefines =
|
||||
treeWithValues.definesVarNames.value --
|
||||
argsFull.streamArgs.keySet --
|
||||
argsFull.streamArgs.values.map(_.name) --
|
||||
call.exportTo.filter { exp =>
|
||||
exp.`type` match {
|
||||
case StreamType(_) => false
|
||||
case _ => true
|
||||
}
|
||||
}.map(_.name)
|
||||
|
||||
// We have some names in scope (forbiddenNames), can't introduce them again; so find new names
|
||||
shouldRename <- Mangler[S].findNewNames(treeDefines)
|
||||
_ <- Mangler[S].forbid(treeDefines ++ shouldRename.values.toSet)
|
||||
|
||||
// If there was a collision, rename exports and usages with new names
|
||||
treeRenamed = treeWithValues.rename(shouldRename)
|
||||
|
||||
// Result could be derived from arguments, or renamed; take care about that
|
||||
result: List[ValueRaw] = fn.ret.map(_.resolveWith(argsToData)).map {
|
||||
case v: VarRaw if shouldRename.contains(v.name) => v.copy(shouldRename(v.name))
|
||||
case v => v
|
||||
}
|
||||
} yield (treeRenamed.tree, result)
|
||||
|
||||
private def handleTree[S: Exports: Counter: Mangler: Arrows](
|
||||
tree: FuncOp.Tree
|
||||
): State[S, FuncOp.Tree] =
|
||||
FuncOp.traverseS(tree, handleTag(_))
|
||||
|
||||
// resolve values of this tag with resolved exports, lift to Cofree as a leaf
|
||||
private def resolveLeaf[S: Exports](tag: RawTag): State[S, FuncOp.Tree] =
|
||||
Exports[S].exports.map(resolvedExports =>
|
||||
FuncOp.leaf(tag.mapValues(_.resolveWith(resolvedExports))).tree
|
||||
)
|
||||
|
||||
private def callArrow[S: Exports: Counter: Arrows: Mangler](
|
||||
arrow: FuncArrow,
|
||||
call: Call
|
||||
): State[S, FuncOp.Tree] =
|
||||
for {
|
||||
callResolved <- Exports[S].resolveCall(call)
|
||||
|
||||
// HERE we take values and desugarize them!
|
||||
cd <- Sugar.desugarize(callResolved)
|
||||
(callDesugarized, maybePrelude) = cd
|
||||
|
||||
passArrows <- Arrows[S].pickArrows(callResolved.arrowArgNames)
|
||||
noNames <- Mangler[S].getForbiddenNames
|
||||
|
||||
av <- Arrows[S].scope(
|
||||
for {
|
||||
_ <- Arrows[S].resolved(passArrows)
|
||||
av <- ArrowInliner.inline(arrow, callDesugarized)
|
||||
} yield av
|
||||
)
|
||||
(appliedOp, value) = av
|
||||
|
||||
// If smth needs to be added before this function tree, add it with SeqTag
|
||||
fullOp = maybePrelude.fold(appliedOp)(prelude =>
|
||||
FuncOp.node(SeqTag, Chain(prelude, appliedOp))
|
||||
)
|
||||
|
||||
// Function defines new names inside its body – need to collect them
|
||||
// TODO: actually it's done and dropped – so keep and pass it instead
|
||||
// (maybe it's already done btw, maybe we don't need to do anything)
|
||||
newNames = fullOp.definesVarNames.value
|
||||
|
||||
_ <- Counter[S].incr
|
||||
_ <- Mangler[S].forbid(newNames)
|
||||
_ <- Exports[S].resolved(call.exportTo.map(_.name).zip(value).toMap)
|
||||
|
||||
} yield fullOp.tree
|
||||
|
||||
private def handleTag[S: Exports: Counter: Arrows: Mangler](tag: RawTag): State[S, FuncOp.Tree] =
|
||||
Arrows[S].arrows.flatMap(resolvedArrows =>
|
||||
tag match {
|
||||
case CallArrowTag(fn, c) if resolvedArrows.contains(fn) =>
|
||||
callArrow(resolvedArrows(fn), c)
|
||||
|
||||
case ClosureTag(arrow) =>
|
||||
for {
|
||||
_ <- Arrows[S].resolved(arrow)
|
||||
tree <- resolveLeaf(tag)
|
||||
} yield tree
|
||||
|
||||
case AssignmentTag(value, assignTo) =>
|
||||
for {
|
||||
_ <- Exports[S].resolved(assignTo, value)
|
||||
tree <- resolveLeaf(tag)
|
||||
} yield tree
|
||||
|
||||
case CallArrowTag(fn, _) =>
|
||||
logger.error(
|
||||
s"UNRESOLVED arrow $fn, skipping, will become (null) in AIR! Known arrows: ${resolvedArrows.keySet}"
|
||||
)
|
||||
resolveLeaf(tag)
|
||||
|
||||
case _ =>
|
||||
resolveLeaf(tag)
|
||||
}
|
||||
)
|
||||
|
||||
}
|
83
model/src/main/scala/aqua/model/inline/Sugar.scala
Normal file
83
model/src/main/scala/aqua/model/inline/Sugar.scala
Normal file
@ -0,0 +1,83 @@
|
||||
package aqua.model.inline
|
||||
|
||||
import aqua.model.inline.state.Counter
|
||||
import aqua.model.{IntoFieldModel, IntoIndexModel, LambdaModel, LiteralModel, ValueModel, VarModel}
|
||||
import aqua.raw.ops.{Call, FlattenTag, FuncOp, ParTag, SeqTag}
|
||||
import aqua.raw.value.{IntoFieldRaw, IntoIndexRaw, LambdaRaw, LiteralRaw, ValueRaw, VarRaw}
|
||||
import cats.data.{Chain, State}
|
||||
import cats.syntax.traverse.*
|
||||
import cats.instances.list.*
|
||||
|
||||
object Sugar {
|
||||
|
||||
private def unfold(raw: ValueRaw, i: Int): (ValueModel, Map[String, ValueRaw]) = raw match {
|
||||
case VarRaw(name, t, lambda) if lambda.isEmpty =>
|
||||
VarModel(name, t, Chain.empty) -> Map.empty
|
||||
case LiteralRaw(value, t) =>
|
||||
LiteralModel(value, t) -> Map.empty
|
||||
case VarRaw(name, t, lambda) =>
|
||||
val (lambdaModel, map) =
|
||||
lambda.foldLeft(Chain.empty[LambdaModel] -> Map.empty[String, ValueRaw]) {
|
||||
case ((lc, m), l) =>
|
||||
val (lm, mm) = unfoldLambda(l, i + m.size)
|
||||
(lc :+ lm, m ++ mm)
|
||||
}
|
||||
VarModel(name, t, lambdaModel) -> map
|
||||
}
|
||||
|
||||
private def unfoldLambda(l: LambdaRaw, i: Int): (LambdaModel, Map[String, ValueRaw]) = l match {
|
||||
case IntoFieldRaw(field, t) => IntoFieldModel(field, t) -> Map.empty
|
||||
case IntoIndexRaw(vm @ VarRaw(name, _, l), t) if l.nonEmpty =>
|
||||
val ni = name + "-" + i
|
||||
IntoIndexModel(ni, t) -> Map(ni -> vm)
|
||||
case IntoIndexRaw(VarRaw(name, _, _), t) =>
|
||||
IntoIndexModel(name, t) -> Map.empty
|
||||
|
||||
case IntoIndexRaw(LiteralRaw(value, _), t) =>
|
||||
IntoIndexModel(value, t) -> Map.empty
|
||||
}
|
||||
|
||||
def desugarize[S: Counter](value: ValueRaw): State[S, (ValueRaw, Option[FuncOp])] =
|
||||
for {
|
||||
i <- Counter[S].get
|
||||
(vm, map) = unfold(value, i)
|
||||
_ <- Counter[S].add(map.size)
|
||||
|
||||
ops <- map.toList.traverse { case (name, v) =>
|
||||
desugarize(v).map {
|
||||
case (vv, Some(op)) =>
|
||||
FuncOp.node(SeqTag, Chain(op, FuncOp.leaf(FlattenTag(v, name))))
|
||||
|
||||
case _ =>
|
||||
FuncOp.leaf(FlattenTag(v, name))
|
||||
}
|
||||
}
|
||||
|
||||
// Take values from a chain
|
||||
// for each, recursiveRaw
|
||||
// group all into par
|
||||
// for each, recursiveRaw
|
||||
// if anything returned, put it into seq before this
|
||||
} yield (
|
||||
vm.toRaw,
|
||||
ops match {
|
||||
case Nil => None
|
||||
case x :: Nil => Option(x)
|
||||
case _ => Option(FuncOp.node(ParTag, Chain.fromSeq(ops)))
|
||||
}
|
||||
)
|
||||
|
||||
def desugarize[S: Counter](
|
||||
values: List[ValueRaw]
|
||||
): State[S, List[(ValueRaw, Option[FuncOp])]] =
|
||||
values.traverse(desugarize(_))
|
||||
|
||||
def desugarize[S: Counter](call: Call): State[S, (Call, Option[FuncOp])] =
|
||||
desugarize(call.args).map(list =>
|
||||
call.copy(list.map(_._1)) -> (list.flatMap(_._2) match {
|
||||
case Nil => None
|
||||
case x :: Nil => Option(x)
|
||||
case vs => Option(FuncOp.node(ParTag, Chain.fromSeq(vs)))
|
||||
})
|
||||
)
|
||||
}
|
106
model/src/main/scala/aqua/model/inline/state/Arrows.scala
Normal file
106
model/src/main/scala/aqua/model/inline/state/Arrows.scala
Normal file
@ -0,0 +1,106 @@
|
||||
package aqua.model.inline.state
|
||||
|
||||
import aqua.raw.arrow.{ArgsCall, FuncArrow, FuncRaw}
|
||||
import cats.data.State
|
||||
import cats.instances.list.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.traverse.*
|
||||
|
||||
/**
|
||||
* State algebra for resolved arrows
|
||||
*
|
||||
* @tparam S State
|
||||
*/
|
||||
trait Arrows[S] extends Scoped[S] {
|
||||
self =>
|
||||
def save(name: String, arrow: FuncArrow): State[S, Unit]
|
||||
|
||||
/**
|
||||
* Arrow is resolved – save it to the state [[S]]
|
||||
*
|
||||
* @param arrow resolved arrow
|
||||
* @param e contextual Exports that an arrow captures
|
||||
*/
|
||||
final def resolved(arrow: FuncRaw)(implicit e: Exports[S]): State[S, Unit] =
|
||||
for {
|
||||
exps <- e.exports
|
||||
arrs <- arrows
|
||||
funcArrow = arrow.capture(arrs, exps)
|
||||
_ <- save(arrow.name, funcArrow)
|
||||
} yield ()
|
||||
|
||||
/**
|
||||
* Save arrows to the state [[S]]
|
||||
*
|
||||
* @param arrows Resolved arrows, accessible by key name which could differ from arrow's name
|
||||
*/
|
||||
final def resolved(arrows: Map[String, FuncArrow]): State[S, Unit] =
|
||||
arrows.toList.traverse(save).void
|
||||
|
||||
/**
|
||||
* All arrows available for use in scope
|
||||
*/
|
||||
val arrows: State[S, Map[String, FuncArrow]]
|
||||
|
||||
/**
|
||||
* Pick a subset of arrows by names
|
||||
*
|
||||
* @param names What arrows should be taken
|
||||
*/
|
||||
def pickArrows(names: Set[String]): State[S, Map[String, FuncArrow]] =
|
||||
arrows.map(_.view.filterKeys(names).toMap)
|
||||
|
||||
/**
|
||||
* Take arrows selected by the function call arguments
|
||||
*
|
||||
* @param args
|
||||
* @return
|
||||
*/
|
||||
def argsArrows(args: ArgsCall): State[S, Map[String, FuncArrow]] =
|
||||
arrows.map(args.arrowArgs)
|
||||
|
||||
/**
|
||||
* Changes the [[S]] type to [[R]]
|
||||
*
|
||||
* @param f Lens getter
|
||||
* @param g Lens setter
|
||||
* @tparam R New state type
|
||||
*/
|
||||
def transformS[R](f: R => S, g: (R, S) => R): Arrows[R] = new Arrows[R] {
|
||||
|
||||
override def save(name: String, arrow: FuncArrow): State[R, Unit] =
|
||||
self.save(name, arrow).transformS(f, g)
|
||||
|
||||
override val arrows: State[R, Map[String, FuncArrow]] = self.arrows.transformS(f, g)
|
||||
|
||||
override val purge: State[R, R] =
|
||||
self.purgeR(f, g)
|
||||
|
||||
override protected def fill(s: R): State[R, Unit] =
|
||||
self.fillR(s, f, g)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
object Arrows {
|
||||
def apply[S](implicit arrows: Arrows[S]): Arrows[S] = arrows
|
||||
|
||||
// Default implementation with the most straightforward state – just a Map
|
||||
object Simple extends Arrows[Map[String, FuncArrow]] {
|
||||
|
||||
override def save(name: String, arrow: FuncArrow): State[Map[String, FuncArrow], Unit] =
|
||||
State.modify(_ + (name -> arrow))
|
||||
|
||||
override val arrows: State[Map[String, FuncArrow], Map[String, FuncArrow]] =
|
||||
State.get
|
||||
|
||||
override val purge: State[Map[String, FuncArrow], Map[String, FuncArrow]] =
|
||||
for {
|
||||
s <- State.get
|
||||
_ <- State.set(Map.empty)
|
||||
} yield s
|
||||
|
||||
override protected def fill(s: Map[String, FuncArrow]): State[Map[String, FuncArrow], Unit] =
|
||||
State.set(s)
|
||||
}
|
||||
}
|
40
model/src/main/scala/aqua/model/inline/state/Counter.scala
Normal file
40
model/src/main/scala/aqua/model/inline/state/Counter.scala
Normal file
@ -0,0 +1,40 @@
|
||||
package aqua.model.inline.state
|
||||
|
||||
import cats.data.State
|
||||
import cats.syntax.flatMap.*
|
||||
|
||||
/**
|
||||
* Monotonic counter, stored within the State monad
|
||||
*
|
||||
* @tparam S
|
||||
* State
|
||||
*/
|
||||
trait Counter[S] {
|
||||
self =>
|
||||
// Get counter
|
||||
val get: State[S, Int]
|
||||
|
||||
// Increment by i
|
||||
def add(i: Int): State[S, Unit]
|
||||
|
||||
// Increment by 1 and get
|
||||
val incr: State[S, Int] = add(1) >> get
|
||||
|
||||
// Change state [[S]] to [[R]]
|
||||
def transformS[R](f: R => S, g: (R, S) => R): Counter[R] = new Counter[R] {
|
||||
override val get: State[R, Int] = self.get.transformS(f, g)
|
||||
|
||||
override def add(i: Int): State[R, Unit] = self.add(i).transformS(f, g)
|
||||
}
|
||||
}
|
||||
|
||||
object Counter {
|
||||
def apply[S](implicit counter: Counter[S]): Counter[S] = counter
|
||||
|
||||
object Simple extends Counter[Int] {
|
||||
|
||||
override val get: State[Int, Int] = State.get
|
||||
|
||||
override def add(i: Int): State[Int, Unit] = State.modify[Int](_ + i)
|
||||
}
|
||||
}
|
85
model/src/main/scala/aqua/model/inline/state/Exports.scala
Normal file
85
model/src/main/scala/aqua/model/inline/state/Exports.scala
Normal file
@ -0,0 +1,85 @@
|
||||
package aqua.model.inline.state
|
||||
|
||||
import aqua.raw.ops.Call
|
||||
import aqua.raw.value.ValueRaw
|
||||
import cats.data.State
|
||||
|
||||
/**
|
||||
* Exports – trace values available in the scope
|
||||
* @tparam S State
|
||||
*/
|
||||
trait Exports[S] extends Scoped[S] {
|
||||
self =>
|
||||
|
||||
/**
|
||||
* [[value]] is accessible as [[exportName]]
|
||||
* @param exportName Name
|
||||
* @param value Value
|
||||
*/
|
||||
def resolved(exportName: String, value: ValueRaw): State[S, Unit]
|
||||
|
||||
/**
|
||||
* Resolve the whole map of exports
|
||||
* @param exports name -> value
|
||||
*/
|
||||
def resolved(exports: Map[String, ValueRaw]): State[S, Unit]
|
||||
|
||||
/**
|
||||
* Get all the values available in the scope
|
||||
*/
|
||||
val exports: State[S, Map[String, ValueRaw]]
|
||||
|
||||
/**
|
||||
* Put the available resolved values into the [[call]] object
|
||||
* @param call Call
|
||||
* @return Resolved Call
|
||||
*/
|
||||
final def resolveCall(call: Call): State[S, Call] =
|
||||
exports.map(res => call.mapValues(_.resolveWith(res)))
|
||||
|
||||
/**
|
||||
* Change [[S]] to [[R]]
|
||||
*/
|
||||
def transformS[R](f: R => S, g: (R, S) => R): Exports[R] = new Exports[R] {
|
||||
|
||||
override def resolved(exportName: String, value: ValueRaw): State[R, Unit] =
|
||||
self.resolved(exportName, value).transformS(f, g)
|
||||
|
||||
override def resolved(exports: Map[String, ValueRaw]): State[R, Unit] =
|
||||
self.resolved(exports).transformS(f, g)
|
||||
|
||||
override val exports: State[R, Map[String, ValueRaw]] =
|
||||
self.exports.transformS(f, g)
|
||||
|
||||
override val purge: State[R, R] =
|
||||
self.purgeR(f, g)
|
||||
|
||||
override protected def fill(s: R): State[R, Unit] =
|
||||
self.fillR(s, f, g)
|
||||
}
|
||||
}
|
||||
|
||||
object Exports {
|
||||
def apply[S](implicit exports: Exports[S]): Exports[S] = exports
|
||||
|
||||
object Simple extends Exports[Map[String, ValueRaw]] {
|
||||
|
||||
override def resolved(exportName: String, value: ValueRaw): State[Map[String, ValueRaw], Unit] =
|
||||
State.modify(_ + (exportName -> value))
|
||||
|
||||
override def resolved(exports: Map[String, ValueRaw]): State[Map[String, ValueRaw], Unit] =
|
||||
State.modify(_ ++ exports)
|
||||
|
||||
override val exports: State[Map[String, ValueRaw], Map[String, ValueRaw]] =
|
||||
State.get
|
||||
|
||||
override val purge: State[Map[String, ValueRaw], Map[String, ValueRaw]] =
|
||||
for {
|
||||
s <- State.get
|
||||
_ <- State.set(Map.empty)
|
||||
} yield s
|
||||
|
||||
override protected def fill(s: Map[String, ValueRaw]): State[Map[String, ValueRaw], Unit] =
|
||||
State.set(s)
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package aqua.model.inline.state
|
||||
|
||||
import aqua.model.ValueModel
|
||||
import aqua.model.inline.state.{Arrows, Counter, Exports, Mangler}
|
||||
import aqua.raw.arrow.{FuncArrow, FuncRaw}
|
||||
import aqua.raw.value.{ValueRaw, VarRaw}
|
||||
import aqua.types.ArrowType
|
||||
import cats.data.{Chain, State}
|
||||
import cats.instances.list.*
|
||||
import cats.syntax.traverse.*
|
||||
import scribe.Logging
|
||||
|
||||
/**
|
||||
* Default states aggregate for all available state algebras
|
||||
*
|
||||
* @param noNames for [[Mangler]]
|
||||
* @param resolvedExports for [[Exports]]
|
||||
* @param resolvedArrows for [[Arrows]]
|
||||
* @param instructionCounter for [[Counter]]
|
||||
*/
|
||||
case class InliningState(
|
||||
noNames: Set[String] = Set.empty,
|
||||
resolvedExports: Map[String, ValueRaw] = Map.empty,
|
||||
resolvedArrows: Map[String, FuncArrow] = Map.empty,
|
||||
instructionCounter: Int = 0
|
||||
)
|
||||
|
||||
object InliningState {
|
||||
|
||||
given Counter[InliningState] =
|
||||
Counter.Simple.transformS(_.instructionCounter, (acc, i) => acc.copy(instructionCounter = i))
|
||||
|
||||
given Mangler[InliningState] =
|
||||
Mangler.Simple.transformS(_.noNames, (acc, nn) => acc.copy(noNames = nn))
|
||||
|
||||
given Arrows[InliningState] =
|
||||
Arrows.Simple.transformS(_.resolvedArrows, (acc, aa) => acc.copy(resolvedArrows = aa))
|
||||
|
||||
given Exports[InliningState] =
|
||||
Exports.Simple.transformS(_.resolvedExports, (acc, ex) => acc.copy(resolvedExports = ex))
|
||||
|
||||
}
|
47
model/src/main/scala/aqua/model/inline/state/Mangler.scala
Normal file
47
model/src/main/scala/aqua/model/inline/state/Mangler.scala
Normal file
@ -0,0 +1,47 @@
|
||||
package aqua.model.inline.state
|
||||
|
||||
import cats.data.State
|
||||
|
||||
trait Mangler[S] {
|
||||
self =>
|
||||
def getForbiddenNames: State[S, Set[String]]
|
||||
|
||||
def findNewNames(introduce: Set[String]): State[S, Map[String, String]]
|
||||
|
||||
def forbid(names: Set[String]): State[S, Unit]
|
||||
|
||||
def transformS[R](f: R => S, g: (R, S) => R): Mangler[R] =
|
||||
new Mangler[R] {
|
||||
|
||||
val getForbiddenNames: State[R, Set[String]] =
|
||||
self.getForbiddenNames.transformS(f, g)
|
||||
|
||||
def findNewNames(introduce: Set[String]): State[R, Map[String, String]] =
|
||||
self.findNewNames(introduce).transformS(f, g)
|
||||
|
||||
def forbid(names: Set[String]): State[R, Unit] =
|
||||
self.forbid(names).transformS(f, g)
|
||||
}
|
||||
}
|
||||
|
||||
object Mangler {
|
||||
def apply[S](implicit mangler: Mangler[S]): Mangler[S] = mangler
|
||||
|
||||
object Simple extends Mangler[Set[String]] {
|
||||
val getForbiddenNames: State[Set[String], Set[String]] = State.get
|
||||
|
||||
def findNewNames(introduce: Set[String]): State[Set[String], Map[String, String]] =
|
||||
getForbiddenNames.map(forbidden =>
|
||||
(forbidden intersect introduce).foldLeft(Map.empty[String, String]) { case (acc, name) =>
|
||||
acc + (name -> LazyList
|
||||
.from(0)
|
||||
.map(name + _)
|
||||
.dropWhile(n => forbidden(n) || introduce(n) || acc.contains(n))
|
||||
.head)
|
||||
}
|
||||
)
|
||||
|
||||
def forbid(names: Set[String]): State[Set[String], Unit] =
|
||||
State.modify(_ ++ names)
|
||||
}
|
||||
}
|
39
model/src/main/scala/aqua/model/inline/state/Scoped.scala
Normal file
39
model/src/main/scala/aqua/model/inline/state/Scoped.scala
Normal file
@ -0,0 +1,39 @@
|
||||
package aqua.model.inline.state
|
||||
|
||||
import cats.data.State
|
||||
|
||||
/**
|
||||
* Common transformations to make an isolated scope for the state [[S]]
|
||||
* @tparam S State
|
||||
*/
|
||||
trait Scoped[S] {
|
||||
// Remove everything from the state
|
||||
val purge: State[S, S]
|
||||
|
||||
// Put [[s]] to the state
|
||||
protected def fill(s: S): State[S, Unit]
|
||||
|
||||
/**
|
||||
* Clear the state, run [[scoped]], then recover the initial state
|
||||
* @param scoped What to run with empty [[S]]
|
||||
* @tparam T Return type
|
||||
* @return Value returned by [[scoped]]
|
||||
*/
|
||||
def scope[T](scoped: State[S, T]): State[S, T] =
|
||||
for {
|
||||
r <- purge
|
||||
t <- scoped
|
||||
_ <- fill(r)
|
||||
} yield t
|
||||
|
||||
// For transformS
|
||||
protected def purgeR[R](f: R => S, g: (R, S) => R): State[R, R] =
|
||||
for {
|
||||
r <- State.get[R]
|
||||
s <- purge.transformS(f, g)
|
||||
} yield g(r, s)
|
||||
|
||||
// For transformS
|
||||
protected def fillR[R](s: R, f: R => S, g: (R, S) => R): State[R, Unit] =
|
||||
fill(f(s)).transformS(f, g)
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
package aqua
|
||||
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.func.raw.*
|
||||
import aqua.model.transform.TransformConfig
|
||||
import aqua.model.transform.res.{
|
||||
CallRes,
|
||||
@ -11,8 +9,23 @@ import aqua.model.transform.res.{
|
||||
NextRes,
|
||||
ResolvedOp
|
||||
}
|
||||
import aqua.model.ValueModel
|
||||
import aqua.model.transform.funcop.ErrorsCatcher
|
||||
import aqua.model.{LiteralModel, ValueModel, VarModel}
|
||||
import aqua.model.{LiteralModel, VarModel}
|
||||
import aqua.raw.ops.{
|
||||
Call,
|
||||
CallServiceTag,
|
||||
ForTag,
|
||||
FuncOp,
|
||||
MatchMismatchTag,
|
||||
NextTag,
|
||||
OnTag,
|
||||
ParTag,
|
||||
RawTag,
|
||||
SeqTag,
|
||||
XorTag
|
||||
}
|
||||
import aqua.raw.value.{LiteralRaw, ValueRaw, VarRaw}
|
||||
import aqua.types.{ArrayType, LiteralType, ScalarType}
|
||||
import cats.Eval
|
||||
import cats.data.Chain
|
||||
@ -53,18 +66,25 @@ object Node {
|
||||
implicit def funcOpToRaw(op: FuncOp): Raw =
|
||||
op.tree
|
||||
|
||||
val relay = LiteralModel("-relay-", ScalarType.string)
|
||||
val relayV = VarModel("-relay-", ScalarType.string)
|
||||
val initPeer = LiteralModel.initPeerId
|
||||
implicit def rawToValue(raw: ValueRaw): ValueModel = ValueModel.fromRaw(raw)
|
||||
|
||||
val relay = LiteralRaw("-relay-", ScalarType.string)
|
||||
|
||||
val relayV = VarRaw("-relay-", ScalarType.string)
|
||||
|
||||
val initPeer = ValueRaw.InitPeerId
|
||||
|
||||
val emptyCall = Call(Nil, Nil)
|
||||
val otherPeer = LiteralModel("other-peer", ScalarType.string)
|
||||
val otherPeerL = LiteralModel("\"other-peer\"", LiteralType.string)
|
||||
val otherRelay = LiteralModel("other-relay", ScalarType.string)
|
||||
val otherPeer2 = LiteralModel("other-peer-2", ScalarType.string)
|
||||
val otherRelay2 = LiteralModel("other-relay-2", ScalarType.string)
|
||||
val varNode = VarModel("node-id", ScalarType.string)
|
||||
val viaList = VarModel("other-relay-2", ArrayType(ScalarType.string))
|
||||
val valueArray = VarModel("array", ArrayType(ScalarType.string))
|
||||
|
||||
val otherPeer = VarRaw("other-peer", ScalarType.string)
|
||||
|
||||
val otherPeerL = LiteralRaw("\"other-peer\"", LiteralType.string)
|
||||
val otherRelay = LiteralRaw("other-relay", ScalarType.string)
|
||||
val otherPeer2 = LiteralRaw("other-peer-2", ScalarType.string)
|
||||
val otherRelay2 = LiteralRaw("other-relay-2", ScalarType.string)
|
||||
val varNode = VarRaw("node-id", ScalarType.string)
|
||||
val viaList = VarRaw("other-relay-2", ArrayType(ScalarType.string))
|
||||
val valueArray = VarRaw("array", ArrayType(ScalarType.string))
|
||||
|
||||
def callRes(
|
||||
i: Int,
|
||||
@ -72,12 +92,12 @@ object Node {
|
||||
exportTo: Option[Call.Export] = None,
|
||||
args: List[ValueModel] = Nil
|
||||
): Res = Node(
|
||||
CallServiceRes(LiteralModel(s"srv$i", ScalarType.string), s"fn$i", CallRes(args, exportTo), on)
|
||||
CallServiceRes(VarRaw(s"srv$i", ScalarType.string), s"fn$i", CallRes(args, exportTo), on)
|
||||
)
|
||||
|
||||
def callTag(i: Int, exportTo: List[Call.Export] = Nil, args: List[ValueModel] = Nil): Raw =
|
||||
def callTag(i: Int, exportTo: List[Call.Export] = Nil, args: List[ValueRaw] = Nil): Raw =
|
||||
Node(
|
||||
CallServiceTag(LiteralModel(s"srv$i", ScalarType.string), s"fn$i", Call(args, exportTo))
|
||||
CallServiceTag(VarRaw(s"srv$i", ScalarType.string), s"fn$i", Call(args, exportTo))
|
||||
)
|
||||
|
||||
def callLiteralRes(i: Int, on: ValueModel, exportTo: Option[Call.Export] = None): Res = Node(
|
||||
@ -91,7 +111,7 @@ object Node {
|
||||
|
||||
def callLiteralRaw(i: Int, exportTo: List[Call.Export] = Nil): Raw = Node(
|
||||
CallServiceTag(
|
||||
LiteralModel("\"srv" + i + "\"", LiteralType.string),
|
||||
LiteralRaw.quote("srv" + i),
|
||||
s"fn$i",
|
||||
Call(Nil, exportTo)
|
||||
)
|
||||
@ -99,10 +119,10 @@ object Node {
|
||||
|
||||
def errorCall(bc: TransformConfig, i: Int, on: ValueModel = initPeer): Res = Node[ResolvedOp](
|
||||
CallServiceRes(
|
||||
bc.errorHandlingCallback,
|
||||
ValueModel.fromRaw(bc.errorHandlingCallback),
|
||||
bc.errorFuncName,
|
||||
CallRes(
|
||||
ErrorsCatcher.lastErrorArg :: LiteralModel(
|
||||
ValueModel.fromRaw(ErrorsCatcher.lastErrorArg) :: LiteralModel(
|
||||
i.toString,
|
||||
LiteralType.number
|
||||
) :: Nil,
|
||||
@ -115,7 +135,7 @@ object Node {
|
||||
def respCall(bc: TransformConfig, value: ValueModel, on: ValueModel = initPeer): Res =
|
||||
Node[ResolvedOp](
|
||||
CallServiceRes(
|
||||
bc.callbackSrvId,
|
||||
ValueModel.fromRaw(bc.callbackSrvId),
|
||||
bc.respFuncName,
|
||||
CallRes(value :: Nil, None),
|
||||
on
|
||||
@ -125,20 +145,20 @@ object Node {
|
||||
def dataCall(bc: TransformConfig, name: String, on: ValueModel = initPeer): Res =
|
||||
Node[ResolvedOp](
|
||||
CallServiceRes(
|
||||
bc.dataSrvId,
|
||||
ValueModel.fromRaw(bc.dataSrvId),
|
||||
name,
|
||||
CallRes(Nil, Some(Call.Export(name, ScalarType.string))),
|
||||
on
|
||||
)
|
||||
)
|
||||
|
||||
def fold(item: String, iter: ValueModel, body: Raw*) =
|
||||
def fold(item: String, iter: ValueRaw, body: Raw*) =
|
||||
Node(
|
||||
ForTag(item, iter),
|
||||
body.toList :+ next(item)
|
||||
)
|
||||
|
||||
def foldPar(item: String, iter: ValueModel, body: Raw*) = {
|
||||
def foldPar(item: String, iter: ValueRaw, body: Raw*) = {
|
||||
val ops = Node(SeqTag, body.toList)
|
||||
Node(
|
||||
ForTag(item, iter),
|
||||
@ -148,13 +168,13 @@ object Node {
|
||||
)
|
||||
}
|
||||
|
||||
def co(body: Raw*) =
|
||||
def co(body: Raw*) =
|
||||
Node(
|
||||
ParTag.Detach,
|
||||
body.toList
|
||||
)
|
||||
|
||||
def on(peer: ValueModel, via: List[ValueModel], body: Raw*) =
|
||||
def on(peer: ValueRaw, via: List[ValueRaw], body: Raw*) =
|
||||
Node(
|
||||
OnTag(peer, Chain.fromSeq(via)),
|
||||
body.toList
|
||||
@ -177,7 +197,7 @@ object Node {
|
||||
body :: Nil
|
||||
)
|
||||
|
||||
def matchRaw(l: ValueModel, r: ValueModel, body: Raw): Raw =
|
||||
def matchRaw(l: ValueRaw, r: ValueRaw, body: Raw): Raw =
|
||||
Node(
|
||||
MatchMismatchTag(l, r, shouldMatch = true),
|
||||
body :: Nil
|
||||
@ -186,10 +206,11 @@ object Node {
|
||||
def through(peer: ValueModel): Node[ResolvedOp] =
|
||||
cofToNode(MakeRes.noop(peer))
|
||||
|
||||
private def equalOrNot[TT](left: TT, right: TT): String = (if (left == right)
|
||||
Console.GREEN + left + Console.RESET
|
||||
else
|
||||
Console.BLUE + left + Console.RED + " != " + Console.YELLOW + right)
|
||||
private def equalOrNot[TT](left: TT, right: TT): String =
|
||||
(if (left == right)
|
||||
Console.GREEN + left + Console.RESET
|
||||
else
|
||||
Console.BLUE + left + Console.RED + " != " + Console.YELLOW + right)
|
||||
|
||||
private def diffArg(left: ValueModel, right: ValueModel): String =
|
||||
Console.GREEN + "(" +
|
||||
|
@ -1,11 +1,12 @@
|
||||
package aqua.model.transform
|
||||
|
||||
import aqua.Node
|
||||
import aqua.model.func.raw.{CallArrowTag, CallServiceTag, FuncOp, FuncOps}
|
||||
import aqua.model.func.{Call, FuncCallable}
|
||||
import aqua.raw.arrow.FuncArrow
|
||||
import aqua.model.transform.res.{CallRes, CallServiceRes, MakeRes}
|
||||
import aqua.model.transform.{Transform, TransformConfig}
|
||||
import aqua.model.{LiteralModel, VarModel}
|
||||
import aqua.raw.ops.{Call, CallArrowTag, CallServiceTag, FuncOp, FuncOps}
|
||||
import aqua.raw.value.{LiteralRaw, VarRaw}
|
||||
import aqua.types.{ArrowType, NilType, ProductType, ScalarType}
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
@ -17,10 +18,10 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
||||
|
||||
"transform.forClient" should "work well with function 1 (no calls before on), generate correct error handling" in {
|
||||
|
||||
val ret = LiteralModel.quote("return this")
|
||||
val ret = LiteralRaw.quote("return this")
|
||||
|
||||
val func: FuncCallable =
|
||||
FuncCallable(
|
||||
val func: FuncArrow =
|
||||
FuncArrow(
|
||||
"ret",
|
||||
on(otherPeer, otherRelay :: Nil, callTag(1)),
|
||||
stringArrow,
|
||||
@ -31,7 +32,7 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
||||
|
||||
val bc = TransformConfig()
|
||||
|
||||
val fc = Transform.fn(func, bc)
|
||||
val fc = Transform.funcRes(func, bc)
|
||||
|
||||
val procFC: Node.Res = fc.body
|
||||
|
||||
@ -61,7 +62,7 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
||||
errorCall(bc, 3, initPeer)
|
||||
)
|
||||
|
||||
//println(procFC)
|
||||
// println(procFC)
|
||||
|
||||
Node.equalsOrPrintDiff(procFC, expectedFC) should be(true)
|
||||
|
||||
@ -69,9 +70,9 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
||||
|
||||
"transform.forClient" should "work well with function 2 (with a call before on)" in {
|
||||
|
||||
val ret = LiteralModel.quote("return this")
|
||||
val ret = LiteralRaw.quote("return this")
|
||||
|
||||
val func: FuncCallable = FuncCallable(
|
||||
val func: FuncArrow = FuncArrow(
|
||||
"ret",
|
||||
FuncOps.seq(callTag(0), on(otherPeer, Nil, callTag(1))),
|
||||
stringArrow,
|
||||
@ -82,7 +83,7 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
||||
|
||||
val bc = TransformConfig(wrapWithXor = false)
|
||||
|
||||
val fc = Transform.fn(func, bc)
|
||||
val fc = Transform.funcRes(func, bc)
|
||||
|
||||
val procFC: Res = fc.body
|
||||
|
||||
@ -111,53 +112,54 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
||||
<- variable
|
||||
*/
|
||||
|
||||
val f1: FuncCallable =
|
||||
FuncCallable(
|
||||
val f1: FuncArrow =
|
||||
FuncArrow(
|
||||
"f1",
|
||||
FuncOp(
|
||||
Node(
|
||||
CallServiceTag(
|
||||
LiteralModel.quote("srv1"),
|
||||
LiteralRaw.quote("srv1"),
|
||||
"foo",
|
||||
Call(Nil, Call.Export("v", ScalarType.string) :: Nil)
|
||||
)
|
||||
).cof
|
||||
),
|
||||
stringArrow,
|
||||
VarModel("v", ScalarType.string) :: Nil,
|
||||
VarRaw("v", ScalarType.string) :: Nil,
|
||||
Map.empty,
|
||||
Map.empty
|
||||
)
|
||||
|
||||
val f2: FuncCallable =
|
||||
FuncCallable(
|
||||
val f2: FuncArrow =
|
||||
FuncArrow(
|
||||
"f2",
|
||||
FuncOp(
|
||||
Node(CallArrowTag("callable", Call(Nil, Call.Export("v", ScalarType.string) :: Nil))).cof
|
||||
),
|
||||
stringArrow,
|
||||
VarModel("v", ScalarType.string) :: Nil,
|
||||
VarRaw("v", ScalarType.string) :: Nil,
|
||||
Map("callable" -> f1),
|
||||
Map.empty
|
||||
)
|
||||
|
||||
val bc = TransformConfig(wrapWithXor = false)
|
||||
|
||||
val res = Transform.fn(f2, bc).body: Node.Res
|
||||
val res = Transform.funcRes(f2, bc).body: Node.Res
|
||||
|
||||
res.equalsOrPrintDiff(
|
||||
MakeRes.seq(
|
||||
dataCall(bc, "-relay-", initPeer),
|
||||
Node(
|
||||
CallServiceRes(
|
||||
LiteralModel.quote("srv1"),
|
||||
LiteralRaw.quote("srv1"),
|
||||
"foo",
|
||||
CallRes(Nil, Some(Call.Export("v", ScalarType.string))),
|
||||
initPeer
|
||||
)
|
||||
),
|
||||
respCall(bc, VarModel("v", ScalarType.string), initPeer)
|
||||
respCall(bc, VarRaw("v", ScalarType.string), initPeer)
|
||||
)
|
||||
) should be(true)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
package aqua.model.transform.topology
|
||||
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.func.raw.{FuncOp, FuncOps, OnTag, ReturnTag}
|
||||
import aqua.raw.ops.FuncOps
|
||||
import aqua.model.transform.cursor.ChainZipper
|
||||
import aqua.model.{LiteralModel, ValueModel, VarModel}
|
||||
import aqua.raw.value.{LiteralRaw, ValueRaw, VarRaw}
|
||||
import aqua.raw.ops.{Call, FuncOp, OnTag, ReturnTag}
|
||||
import aqua.raw.value.{ValueRaw, VarRaw}
|
||||
import aqua.types.{ArrayType, ScalarType}
|
||||
import cats.data.{Chain, NonEmptyList}
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
@ -11,16 +12,16 @@ import org.scalatest.matchers.should.Matchers
|
||||
|
||||
class RawCursorSpec extends AnyFlatSpec with Matchers {
|
||||
import FuncOp.*
|
||||
import FuncOps.*
|
||||
import aqua.raw.ops.FuncOps.*
|
||||
|
||||
"simple raw cursor on init_peer_id" should "move properly" in {
|
||||
val raw = RawCursor(
|
||||
NonEmptyList.one(
|
||||
ChainZipper.one(
|
||||
onVia(
|
||||
LiteralModel.initPeerId,
|
||||
ValueRaw.InitPeerId,
|
||||
Chain.empty,
|
||||
callService(LiteralModel.quote("calledOutside"), "fn", Call(Nil, Nil))
|
||||
callService(LiteralRaw.quote("calledOutside"), "fn", Call(Nil, Nil))
|
||||
).tree
|
||||
)
|
||||
)
|
||||
@ -34,13 +35,13 @@ class RawCursorSpec extends AnyFlatSpec with Matchers {
|
||||
NonEmptyList.one(
|
||||
ChainZipper.one(
|
||||
onVia(
|
||||
LiteralModel.initPeerId,
|
||||
ValueRaw.InitPeerId,
|
||||
Chain.empty,
|
||||
seq(
|
||||
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(LiteralRaw.quote("1"), "fn", Call(Nil, Nil)),
|
||||
callService(LiteralRaw.quote("2"), "fn", Call(Nil, Nil)),
|
||||
callService(LiteralRaw.quote("3"), "fn", Call(Nil, Nil)),
|
||||
callService(LiteralRaw.quote("4"), "fn", Call(Nil, Nil))
|
||||
)
|
||||
).tree
|
||||
)
|
||||
@ -58,9 +59,9 @@ class RawCursorSpec extends AnyFlatSpec with Matchers {
|
||||
NonEmptyList.one(
|
||||
ChainZipper.one(
|
||||
onVia(
|
||||
LiteralModel.initPeerId,
|
||||
Chain.one(VarModel("-relay-", ScalarType.string)),
|
||||
callService(LiteralModel.quote("calledOutside"), "fn", Call(Nil, Nil))
|
||||
ValueRaw.InitPeerId,
|
||||
Chain.one(VarRaw("-relay-", ScalarType.string)),
|
||||
callService(LiteralRaw.quote("calledOutside"), "fn", Call(Nil, Nil))
|
||||
).tree
|
||||
)
|
||||
)
|
||||
@ -75,26 +76,26 @@ class RawCursorSpec extends AnyFlatSpec with Matchers {
|
||||
NonEmptyList.one(
|
||||
ChainZipper.one(
|
||||
onVia(
|
||||
LiteralModel.initPeerId,
|
||||
Chain.one(VarModel("-relay-", ScalarType.string)),
|
||||
ValueRaw.InitPeerId,
|
||||
Chain.one(VarRaw("-relay-", ScalarType.string)),
|
||||
seq(
|
||||
callService(LiteralModel.quote("calledOutside"), "fn", Call(Nil, Nil)),
|
||||
callService(LiteralRaw.quote("calledOutside"), "fn", Call(Nil, Nil)),
|
||||
onVia(
|
||||
VarModel("-other-", ScalarType.string),
|
||||
Chain.one(VarModel("-external-", ScalarType.string)),
|
||||
VarRaw("-other-", ScalarType.string),
|
||||
Chain.one(VarRaw("-external-", ScalarType.string)),
|
||||
seq(
|
||||
callService(
|
||||
LiteralModel.quote("calledInside"),
|
||||
LiteralRaw.quote("calledInside"),
|
||||
"fn",
|
||||
Call(Nil, Call.Export("export", ScalarType.string) :: Nil)
|
||||
),
|
||||
leaf(ReturnTag(NonEmptyList.one(VarModel("export", ScalarType.string))))
|
||||
leaf(ReturnTag(NonEmptyList.one(VarRaw("export", ScalarType.string))))
|
||||
)
|
||||
),
|
||||
callService(
|
||||
LiteralModel.quote("return"),
|
||||
LiteralRaw.quote("return"),
|
||||
"fn",
|
||||
Call(VarModel("export", ScalarType.string) :: Nil, Nil)
|
||||
Call(VarRaw("export", ScalarType.string) :: Nil, Nil)
|
||||
)
|
||||
)
|
||||
).tree
|
||||
@ -103,26 +104,26 @@ class RawCursorSpec extends AnyFlatSpec with Matchers {
|
||||
)
|
||||
|
||||
raw.tag should be(
|
||||
OnTag(LiteralModel.initPeerId, Chain.one(VarModel("-relay-", ScalarType.string)))
|
||||
OnTag(ValueRaw.InitPeerId, Chain.one(VarRaw("-relay-", ScalarType.string)))
|
||||
)
|
||||
// raw.firstExecuted.map(_.tag) should be(
|
||||
// Some(
|
||||
// callService(LiteralModel.quote("calledOutside"), "fn", Call(Nil, Nil)).tree.head
|
||||
// callService(LiteralRaw.quote("calledOutside"), "fn", Call(Nil, Nil)).tree.head
|
||||
// )
|
||||
// )
|
||||
// raw.lastExecuted.map(_.tag) should be(
|
||||
// Some(
|
||||
// callService(
|
||||
// LiteralModel.quote("return"),
|
||||
// LiteralRaw.quote("return"),
|
||||
// "fn",
|
||||
// Call(VarModel("export", ScalarType.string) :: Nil, Nil)
|
||||
// Call(VarRaw("export", ScalarType.string) :: Nil, Nil)
|
||||
// ).tree.head
|
||||
// )
|
||||
// )
|
||||
// raw.lastExecuted.flatMap(_.seqPrev).flatMap(_.lastExecuted).map(_.tag) should be(
|
||||
// Some(
|
||||
// callService(
|
||||
// LiteralModel.quote("calledInside"),
|
||||
// LiteralRaw.quote("calledInside"),
|
||||
// "fn",
|
||||
// Call(Nil, Call.Export("export", ScalarType.string) :: Nil)
|
||||
// ).tree.head
|
||||
@ -137,21 +138,21 @@ class RawCursorSpec extends AnyFlatSpec with Matchers {
|
||||
NonEmptyList.one(
|
||||
ChainZipper.one(
|
||||
onVia(
|
||||
LiteralModel.initPeerId,
|
||||
Chain.one(VarModel("-relay-", ScalarType.string)),
|
||||
ValueRaw.InitPeerId,
|
||||
Chain.one(VarRaw("-relay-", ScalarType.string)),
|
||||
seq(
|
||||
callService(LiteralModel.quote("calledOutside"), "fn", Call(Nil, Nil)),
|
||||
callService(LiteralRaw.quote("calledOutside"), "fn", Call(Nil, Nil)),
|
||||
onVia(
|
||||
VarModel("-other-", ScalarType.string),
|
||||
Chain.one(VarModel("-external-", ScalarType.string)),
|
||||
VarRaw("-other-", ScalarType.string),
|
||||
Chain.one(VarRaw("-external-", ScalarType.string)),
|
||||
fold(
|
||||
"item",
|
||||
VarModel("iterable", ArrayType(ScalarType.string)),
|
||||
VarRaw("iterable", ArrayType(ScalarType.string)),
|
||||
onVia(
|
||||
VarModel("-in-fold-", ScalarType.string),
|
||||
Chain.one(VarModel("-fold-relay-", ScalarType.string)),
|
||||
VarRaw("-in-fold-", ScalarType.string),
|
||||
Chain.one(VarRaw("-fold-relay-", ScalarType.string)),
|
||||
callService(
|
||||
LiteralModel.quote("calledInside"),
|
||||
LiteralRaw.quote("calledInside"),
|
||||
"fn",
|
||||
Call(Nil, Call.Export("export", ScalarType.string) :: Nil)
|
||||
)
|
||||
@ -159,9 +160,9 @@ class RawCursorSpec extends AnyFlatSpec with Matchers {
|
||||
)
|
||||
),
|
||||
callService(
|
||||
LiteralModel.quote("return"),
|
||||
LiteralRaw.quote("return"),
|
||||
"fn",
|
||||
Call(VarModel("export", ScalarType.string) :: Nil, Nil)
|
||||
Call(VarRaw("export", ScalarType.string) :: Nil, Nil)
|
||||
)
|
||||
)
|
||||
).tree
|
||||
@ -170,26 +171,26 @@ class RawCursorSpec extends AnyFlatSpec with Matchers {
|
||||
)
|
||||
|
||||
raw.tag should be(
|
||||
OnTag(LiteralModel.initPeerId, Chain.one(VarModel("-relay-", ScalarType.string)))
|
||||
OnTag(ValueRaw.InitPeerId, Chain.one(VarRaw("-relay-", ScalarType.string)))
|
||||
)
|
||||
// raw.firstExecuted.map(_.tag) should be(
|
||||
// Some(
|
||||
// callService(LiteralModel.quote("calledOutside"), "fn", Call(Nil, Nil)).tree.head
|
||||
// callService(LiteralRaw.quote("calledOutside"), "fn", Call(Nil, Nil)).tree.head
|
||||
// )
|
||||
// )
|
||||
// raw.lastExecuted.map(_.tag) should be(
|
||||
// Some(
|
||||
// callService(
|
||||
// LiteralModel.quote("return"),
|
||||
// LiteralRaw.quote("return"),
|
||||
// "fn",
|
||||
// Call(VarModel("export", ScalarType.string) :: Nil, Nil)
|
||||
// Call(VarRaw("export", ScalarType.string) :: Nil, Nil)
|
||||
// ).tree.head
|
||||
// )
|
||||
// )
|
||||
// raw.lastExecuted.flatMap(_.seqPrev).flatMap(_.lastExecuted).map(_.tag) should be(
|
||||
// Some(
|
||||
// callService(
|
||||
// LiteralModel.quote("calledInside"),
|
||||
// LiteralRaw.quote("calledInside"),
|
||||
// "fn",
|
||||
// Call(Nil, Call.Export("export", ScalarType.string) :: Nil)
|
||||
// ).tree.head
|
||||
@ -197,21 +198,21 @@ class RawCursorSpec extends AnyFlatSpec with Matchers {
|
||||
// )
|
||||
// raw.lastExecuted.flatMap(_.seqPrev).map(_.topology.pathOn).get should be(
|
||||
// OnTag(
|
||||
// VarModel("-in-fold-", ScalarType.string),
|
||||
// Chain.one(VarModel("-fold-relay-", ScalarType.string))
|
||||
// VarRaw("-in-fold-", ScalarType.string),
|
||||
// Chain.one(VarRaw("-fold-relay-", ScalarType.string))
|
||||
// ) :: OnTag(
|
||||
// VarModel("-other-", ScalarType.string),
|
||||
// Chain.one(VarModel("-external-", ScalarType.string))
|
||||
// VarRaw("-other-", ScalarType.string),
|
||||
// Chain.one(VarRaw("-external-", ScalarType.string))
|
||||
// ) :: OnTag(
|
||||
// LiteralModel.initPeerId,
|
||||
// Chain.one(VarModel("-relay-", ScalarType.string))
|
||||
// ValueRaw.InitPeerId,
|
||||
// Chain.one(VarRaw("-relay-", ScalarType.string))
|
||||
// ) :: Nil
|
||||
// )
|
||||
// raw.lastExecuted.map(_.topology.pathBefore).get should be(
|
||||
// Chain(
|
||||
// VarModel("-fold-relay-", ScalarType.string),
|
||||
// VarModel("-external-", ScalarType.string),
|
||||
// VarModel("-relay-", ScalarType.string)
|
||||
// VarRaw("-fold-relay-", ScalarType.string),
|
||||
// VarRaw("-external-", ScalarType.string),
|
||||
// VarRaw("-relay-", ScalarType.string)
|
||||
// )
|
||||
// )
|
||||
|
||||
|
@ -2,9 +2,9 @@ package aqua.model.transform.topology
|
||||
|
||||
import aqua.Node
|
||||
import aqua.model.VarModel
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.func.raw.FuncOps
|
||||
import aqua.model.transform.res.{MakeRes, ResolvedOp, SeqRes, XorRes}
|
||||
import aqua.raw.ops.{Call, FuncOps}
|
||||
import aqua.raw.value.VarRaw
|
||||
import aqua.types.ScalarType
|
||||
import cats.Eval
|
||||
import cats.data.Chain
|
||||
@ -92,7 +92,7 @@ class TopologySpec extends AnyFlatSpec with Matchers {
|
||||
|
||||
"topology resolver" should "build return path in par if there are exported variables" in {
|
||||
val exportTo = Call.Export("result", ScalarType.string) :: Nil
|
||||
val result = VarModel("result", ScalarType.string)
|
||||
val result = VarRaw("result", ScalarType.string)
|
||||
|
||||
val init = on(
|
||||
initPeer,
|
||||
@ -527,7 +527,7 @@ class TopologySpec extends AnyFlatSpec with Matchers {
|
||||
|
||||
// https://github.com/fluencelabs/aqua/issues/205
|
||||
"topology resolver" should "optimize path over fold" in {
|
||||
val i = VarModel("i", ScalarType.string)
|
||||
val i = VarRaw("i", ScalarType.string)
|
||||
val init =
|
||||
on(
|
||||
initPeer,
|
||||
|
@ -1,7 +1,7 @@
|
||||
package aqua.model.transform
|
||||
|
||||
import aqua.model.VarModel
|
||||
import aqua.model.func.FuncCallable
|
||||
import aqua.raw.value.VarRaw
|
||||
import aqua.raw.arrow.FuncArrow
|
||||
import aqua.model.transform.funcop.*
|
||||
import aqua.model.transform.res.{FuncRes, NoAir, ResolvedOp}
|
||||
import aqua.model.transform.topology.Topology
|
||||
@ -27,9 +27,9 @@ object Transform extends Logging {
|
||||
tree.copy(tail = tree.tail.map(_.filter(t => filter(t.head)).map(clear(_, filter))))
|
||||
|
||||
// TODO: doc/rename
|
||||
def fn(func: FuncCallable, conf: TransformConfig): FuncRes = {
|
||||
def funcRes(func: FuncArrow, conf: TransformConfig): FuncRes = {
|
||||
val initCallable: InitPeerCallable = InitViaRelayCallable(
|
||||
Chain.fromOption(conf.relayVarName).map(VarModel(_, ScalarType.string))
|
||||
Chain.fromOption(conf.relayVarName).map(VarRaw(_, ScalarType.string))
|
||||
)
|
||||
val errorsCatcher = ErrorsCatcher(
|
||||
enabled = conf.wrapWithXor,
|
||||
@ -65,8 +65,7 @@ object Transform extends Logging {
|
||||
errorsCatcher
|
||||
.transform(
|
||||
// TODO: comments
|
||||
wrapFunc.
|
||||
resolve(func).value
|
||||
wrapFunc.resolve(func).value
|
||||
)
|
||||
.tree
|
||||
)
|
||||
|
@ -1,6 +1,8 @@
|
||||
package aqua.model.transform
|
||||
|
||||
import aqua.model.{AquaContext, LiteralModel, ValueModel, VarModel}
|
||||
import aqua.model.{LiteralModel, ValueModel, VarModel}
|
||||
import aqua.raw.AquaContext
|
||||
import aqua.raw.value.{LiteralRaw, ValueRaw, VarRaw}
|
||||
import aqua.types.ScalarType
|
||||
import cats.kernel.Monoid
|
||||
|
||||
@ -16,38 +18,38 @@ case class TransformConfig(
|
||||
constants: List[TransformConfig.Const] = Nil
|
||||
) {
|
||||
|
||||
val errorId: ValueModel = LiteralModel.quote(errorFuncName)
|
||||
val errorHandlingCallback: ValueModel = LiteralModel.quote(errorHandlingService)
|
||||
val callbackSrvId: ValueModel = LiteralModel.quote(callbackService)
|
||||
val dataSrvId: ValueModel = LiteralModel.quote(getDataService)
|
||||
val errorId: ValueRaw = LiteralRaw.quote(errorFuncName)
|
||||
val errorHandlingCallback: ValueRaw = LiteralRaw.quote(errorHandlingService)
|
||||
val callbackSrvId: ValueRaw = LiteralRaw.quote(callbackService)
|
||||
val dataSrvId: ValueRaw = LiteralRaw.quote(getDataService)
|
||||
|
||||
// Host peer id holds %init_peer_id% in case Aqua is not compiled to be executed behind a relay,
|
||||
// or relay's variable otherwise
|
||||
val hostPeerId: TransformConfig.Const =
|
||||
TransformConfig.Const(
|
||||
"HOST_PEER_ID",
|
||||
relayVarName.fold[ValueModel](LiteralModel.initPeerId)(r => VarModel(r, ScalarType.string))
|
||||
relayVarName.fold[ValueRaw](ValueRaw.InitPeerId)(r => VarRaw(r, ScalarType.string))
|
||||
)
|
||||
|
||||
val initPeerId: TransformConfig.Const =
|
||||
TransformConfig.Const(
|
||||
"INIT_PEER_ID",
|
||||
LiteralModel.initPeerId
|
||||
ValueRaw.InitPeerId
|
||||
)
|
||||
|
||||
val nil: TransformConfig.Const =
|
||||
TransformConfig.Const(
|
||||
"nil", // TODO: shouldn't it be NIL?
|
||||
LiteralModel.nil
|
||||
ValueRaw.Nil
|
||||
)
|
||||
|
||||
val lastError: TransformConfig.Const =
|
||||
TransformConfig.Const(
|
||||
"LAST_ERROR",
|
||||
VarModel.lastError
|
||||
ValueRaw.LastError
|
||||
)
|
||||
|
||||
val constantsMap: Map[String, ValueModel] =
|
||||
val constantsMap: Map[String, ValueRaw] =
|
||||
(hostPeerId :: initPeerId :: nil :: lastError :: constants)
|
||||
.map(c => c.name -> c.value)
|
||||
.toMap
|
||||
@ -65,7 +67,7 @@ case class TransformConfig(
|
||||
}
|
||||
|
||||
object TransformConfig {
|
||||
case class Const(name: String, value: ValueModel)
|
||||
case class Const(name: String, value: ValueRaw)
|
||||
|
||||
// TODO docs/rename? why it is unused
|
||||
def forHost: TransformConfig =
|
||||
|
@ -1,8 +1,7 @@
|
||||
package aqua.model.transform.funcop
|
||||
|
||||
import aqua.model.{ValueModel, VarModel}
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.func.raw.{FuncOp, FuncOps}
|
||||
import aqua.raw.ops.{Call, FuncOp, FuncOps}
|
||||
import aqua.raw.value.{ValueRaw, VarRaw}
|
||||
import aqua.types.{ArrayType, DataType, StreamType}
|
||||
import cats.data.Chain
|
||||
|
||||
@ -10,7 +9,7 @@ trait ArgsProvider {
|
||||
def transform(op: FuncOp): FuncOp
|
||||
}
|
||||
|
||||
case class ArgsFromService(dataServiceId: ValueModel, names: List[(String, DataType)])
|
||||
case class ArgsFromService(dataServiceId: ValueRaw, names: List[(String, DataType)])
|
||||
extends ArgsProvider {
|
||||
|
||||
private def getStreamDataOp(name: String, t: StreamType): FuncOp = {
|
||||
@ -24,9 +23,9 @@ case class ArgsFromService(dataServiceId: ValueModel, names: List[(String, DataT
|
||||
),
|
||||
FuncOps.fold(
|
||||
item,
|
||||
VarModel(iter, ArrayType(t.element), Chain.empty),
|
||||
VarRaw(iter, ArrayType(t.element)),
|
||||
FuncOps.seq(
|
||||
FuncOps.pushToStream(VarModel(item, t.element), Call.Export(name, t)),
|
||||
FuncOps.pushToStream(VarRaw(item, t.element), Call.Export(name, t)),
|
||||
FuncOps.next(item)
|
||||
)
|
||||
)
|
||||
|
@ -1,8 +1,7 @@
|
||||
package aqua.model.transform.funcop
|
||||
|
||||
import aqua.model.{LiteralModel, ValueModel, VarModel}
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.func.raw.{FuncOp, FuncOps, MatchMismatchTag, OnTag, RawTag, XorTag}
|
||||
import aqua.raw.ops.{Call, FuncOp, FuncOps, MatchMismatchTag, OnTag, RawTag, XorTag}
|
||||
import aqua.raw.value.{LiteralRaw, ValueRaw}
|
||||
import aqua.types.LiteralType
|
||||
import cats.Eval
|
||||
import cats.data.Chain
|
||||
@ -10,7 +9,7 @@ import cats.free.Cofree
|
||||
|
||||
case class ErrorsCatcher(
|
||||
enabled: Boolean,
|
||||
serviceId: ValueModel,
|
||||
serviceId: ValueRaw,
|
||||
funcName: String,
|
||||
callable: InitPeerCallable
|
||||
) {
|
||||
@ -54,10 +53,10 @@ case class ErrorsCatcher(
|
||||
|
||||
object ErrorsCatcher {
|
||||
|
||||
val lastErrorArg: ValueModel = VarModel.lastError
|
||||
val lastErrorArg: ValueRaw = ValueRaw.LastError
|
||||
|
||||
def lastErrorCall(i: Int): Call = Call(
|
||||
lastErrorArg :: LiteralModel(i.toString, LiteralType.number) :: Nil,
|
||||
lastErrorArg :: LiteralRaw.number(i) :: Nil,
|
||||
Nil
|
||||
)
|
||||
}
|
||||
|
@ -1,23 +1,22 @@
|
||||
package aqua.model.transform.funcop
|
||||
|
||||
import aqua.model.{LiteralModel, ValueModel}
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.func.raw.{FuncOp, FuncOps}
|
||||
import aqua.raw.ops.{Call, FuncOp, FuncOps}
|
||||
import aqua.raw.value.ValueRaw
|
||||
import cats.data.Chain
|
||||
|
||||
sealed trait InitPeerCallable {
|
||||
def transform(op: FuncOp): FuncOp
|
||||
|
||||
def makeCall(serviceId: ValueModel, funcName: String, call: Call): FuncOp =
|
||||
def makeCall(serviceId: ValueRaw, funcName: String, call: Call): FuncOp =
|
||||
transform(FuncOps.callService(serviceId, funcName, call))
|
||||
|
||||
def service(serviceId: ValueModel): (String, Call) => FuncOp = makeCall(serviceId, _, _)
|
||||
def service(serviceId: ValueRaw): (String, Call) => FuncOp = makeCall(serviceId, _, _)
|
||||
}
|
||||
|
||||
case class InitViaRelayCallable(goThrough: Chain[ValueModel]) extends InitPeerCallable {
|
||||
case class InitViaRelayCallable(goThrough: Chain[ValueRaw]) extends InitPeerCallable {
|
||||
|
||||
// Get to init user through a relay
|
||||
override def transform(op: FuncOp): FuncOp =
|
||||
FuncOps.onVia(LiteralModel.initPeerId, goThrough, op)
|
||||
FuncOps.onVia(ValueRaw.InitPeerId, goThrough, op)
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
package aqua.model.transform.funcop
|
||||
|
||||
import aqua.model.func.*
|
||||
import aqua.model.func.raw.{FuncOp, FuncOps}
|
||||
import aqua.model.{ValueModel, VarModel}
|
||||
import aqua.model.inline.*
|
||||
import aqua.model.inline.state.InliningState
|
||||
import aqua.raw.ops.FuncOps
|
||||
import aqua.raw.ops.{Call, FuncOp, FuncOps}
|
||||
import aqua.raw.value.{ValueRaw, VarRaw}
|
||||
import aqua.raw.arrow.{ArgsCall, FuncArrow}
|
||||
import aqua.types.*
|
||||
import cats.Eval
|
||||
|
||||
@ -18,7 +21,7 @@ case class ResolveFunc(
|
||||
private val returnVar: String = "-return-"
|
||||
|
||||
// TODO: doc
|
||||
def returnCallback(retModel: List[ValueModel]): FuncOp =
|
||||
def returnCallback(retModel: List[ValueRaw]): FuncOp =
|
||||
callback(
|
||||
respFuncName,
|
||||
Call(
|
||||
@ -28,34 +31,34 @@ case class ResolveFunc(
|
||||
)
|
||||
|
||||
// TODO: doc
|
||||
def arrowToCallback(name: String, arrowType: ArrowType): FuncCallable = {
|
||||
def arrowToCallback(name: String, arrowType: ArrowType): FuncArrow = {
|
||||
val (args, call, ret) = ArgsCall.arrowToArgsCallRet(arrowType)
|
||||
FuncCallable(
|
||||
FuncArrow(
|
||||
arrowCallbackPrefix + name,
|
||||
callback(name, call),
|
||||
arrowType,
|
||||
ret.map(_.model),
|
||||
ret.map(_.toRaw),
|
||||
Map.empty,
|
||||
Map.empty
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: doc/rename
|
||||
def wrap(func: FuncCallable): FuncCallable = {
|
||||
def wrap(func: FuncArrow): FuncArrow = {
|
||||
val returnType = ProductType(func.ret.map(_.lastType).map {
|
||||
// we mustn't return a stream in response callback to avoid pushing stream to `-return-` value
|
||||
case StreamType(t) => ArrayType(t)
|
||||
case t => t
|
||||
}).toLabelledList(returnVar)
|
||||
|
||||
val retModel = returnType.map { case (l, t) => VarModel(l, t) }
|
||||
val retModel = returnType.map { case (l, t) => VarRaw(l, t) }
|
||||
|
||||
val funcCall = Call(
|
||||
func.arrowType.domain.toLabelledList().map(ad => VarModel(ad._1, ad._2)),
|
||||
func.arrowType.domain.toLabelledList().map(ad => VarRaw(ad._1, ad._2)),
|
||||
returnType.map { case (l, t) => Call.Export(l, t) }
|
||||
)
|
||||
|
||||
FuncCallable(
|
||||
FuncArrow(
|
||||
wrapCallableName,
|
||||
transform(
|
||||
FuncOps.seq(
|
||||
@ -82,14 +85,17 @@ case class ResolveFunc(
|
||||
|
||||
// TODO: doc/rename
|
||||
def resolve(
|
||||
func: FuncCallable,
|
||||
func: FuncArrow,
|
||||
funcArgName: String = "_func"
|
||||
): Eval[FuncOp] =
|
||||
wrap(func)
|
||||
.resolve(
|
||||
Call(VarModel(funcArgName, func.arrowType) :: Nil, Nil),
|
||||
Map(funcArgName -> func),
|
||||
Set.empty
|
||||
ArrowInliner
|
||||
.inline[InliningState](
|
||||
wrap(func),
|
||||
Call(VarRaw(funcArgName, func.arrowType) :: Nil, Nil)
|
||||
)
|
||||
.map(_._1)
|
||||
.run(
|
||||
InliningState(resolvedArrows = Map(funcArgName -> func))
|
||||
)
|
||||
.map(_._2)
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
package aqua.model.transform.res
|
||||
|
||||
import aqua.model.AquaContext
|
||||
import aqua.model.transform.res.*
|
||||
import aqua.model.transform.{Transform, TransformConfig}
|
||||
import aqua.raw.AquaContext
|
||||
import cats.data.Chain
|
||||
|
||||
// TODO: doc
|
||||
@ -22,7 +22,7 @@ object AquaRes {
|
||||
.fromSeq(ex.funcs.map { case (fnName, fn) =>
|
||||
fn.copy(funcName = fnName)
|
||||
}.toSeq)
|
||||
.map(Transform.fn(_, conf)),
|
||||
.map(Transform.funcRes(_, conf)),
|
||||
services = Chain
|
||||
.fromSeq(ex.services.map { case (srvName, srv) =>
|
||||
srv.copy(name = srvName)
|
||||
|
@ -1,7 +1,7 @@
|
||||
package aqua.model.transform.res
|
||||
|
||||
import aqua.model.ValueModel
|
||||
import aqua.model.func.Call
|
||||
import aqua.raw.ops.Call
|
||||
|
||||
// TODO docs
|
||||
case class CallRes(args: List[ValueModel], exportTo: Option[Call.Export]) {
|
||||
|
@ -1,16 +1,16 @@
|
||||
package aqua.model.transform.res
|
||||
|
||||
import aqua.model.func.FuncCallable
|
||||
import aqua.model.transform.TransformConfig
|
||||
import aqua.raw.arrow.FuncArrow
|
||||
import aqua.types.{ArrowType, Type}
|
||||
import cats.data.Chain
|
||||
import cats.free.Cofree
|
||||
|
||||
// TODO: docs, why source and body here together?
|
||||
case class FuncRes(
|
||||
source: FuncCallable,
|
||||
conf: TransformConfig,
|
||||
body: Cofree[Chain, ResolvedOp]
|
||||
source: FuncArrow,
|
||||
conf: TransformConfig,
|
||||
body: Cofree[Chain, ResolvedOp]
|
||||
) {
|
||||
import FuncRes.*
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
package aqua.model.transform.res
|
||||
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.func.raw.*
|
||||
import aqua.model.transform.topology.Topology.Res
|
||||
import aqua.model.{LiteralModel, ValueModel, VarModel}
|
||||
import aqua.raw.ops.*
|
||||
import aqua.raw.value.{LiteralRaw, ValueRaw}
|
||||
import aqua.types.{ArrayType, StreamType}
|
||||
import cats.Eval
|
||||
import cats.data.Chain
|
||||
@ -12,6 +12,7 @@ import cats.free.Cofree
|
||||
// TODO docs
|
||||
object MakeRes {
|
||||
val nilTail: Eval[Chain[Res]] = Eval.now(Chain.empty)
|
||||
val op: ValueModel = ValueModel.fromRaw(LiteralRaw.quote("op"))
|
||||
|
||||
def leaf(op: ResolvedOp): Res = Cofree[Chain, ResolvedOp](op, nilTail)
|
||||
|
||||
@ -37,26 +38,32 @@ object MakeRes {
|
||||
)
|
||||
|
||||
def noop(onPeer: ValueModel): Res =
|
||||
leaf(CallServiceRes(LiteralModel.quote("op"), "noop", CallRes(Nil, None), onPeer))
|
||||
leaf(CallServiceRes(op, "noop", CallRes(Nil, None), onPeer))
|
||||
|
||||
def canon(onPeer: ValueModel, operand: ValueModel, target: Call.Export): Res =
|
||||
leaf(
|
||||
CallServiceRes(
|
||||
LiteralModel.quote("op"),
|
||||
op,
|
||||
"identity",
|
||||
CallRes(operand :: Nil, Some(target)),
|
||||
onPeer
|
||||
)
|
||||
)
|
||||
|
||||
private val initPeerId = ValueModel.fromRaw(ValueRaw.InitPeerId)
|
||||
|
||||
private def orInit(currentPeerId: Option[ValueRaw]): ValueModel =
|
||||
currentPeerId.fold(initPeerId)(ValueModel.fromRaw)
|
||||
|
||||
def resolve(
|
||||
currentPeerId: Option[ValueModel],
|
||||
currentPeerId: Option[ValueRaw],
|
||||
i: Int
|
||||
): PartialFunction[RawTag, Res] = {
|
||||
case SeqTag => leaf(SeqRes)
|
||||
case _: OnTag => leaf(SeqRes)
|
||||
case MatchMismatchTag(a, b, s) => leaf(MatchMismatchRes(a, b, s))
|
||||
case ForTag(item, iter) => leaf(FoldRes(item, iter))
|
||||
case MatchMismatchTag(a, b, s) =>
|
||||
leaf(MatchMismatchRes(ValueModel.fromRaw(a), ValueModel.fromRaw(b), s))
|
||||
case ForTag(item, iter) => leaf(FoldRes(item, ValueModel.fromRaw(iter)))
|
||||
case RestrictionTag(item, isStream) => leaf(RestrictionRes(item, isStream))
|
||||
case ParTag | ParTag.Detach => leaf(ParRes)
|
||||
case XorTag | XorTag.LeftBiased => leaf(XorRes)
|
||||
@ -69,32 +76,31 @@ object MakeRes {
|
||||
// RestrictionRes(tmpName, isStream = false),
|
||||
seq(
|
||||
canon(
|
||||
currentPeerId
|
||||
.getOrElse(LiteralModel.initPeerId),
|
||||
operand,
|
||||
orInit(currentPeerId),
|
||||
ValueModel.fromRaw(operand),
|
||||
Call.Export(tmpName, ArrayType(st))
|
||||
),
|
||||
leaf(ApRes(VarModel(tmpName, ArrayType(st)), exportTo))
|
||||
leaf(ApRes(VarModel(tmpName, ArrayType(st), Chain.empty), exportTo))
|
||||
)
|
||||
// )
|
||||
case _ =>
|
||||
leaf(ApRes(operand, exportTo))
|
||||
leaf(ApRes(ValueModel.fromRaw(operand), exportTo))
|
||||
}
|
||||
case CanonicalizeTag(operand, exportTo) =>
|
||||
canon(
|
||||
currentPeerId
|
||||
.getOrElse(LiteralModel.initPeerId),
|
||||
operand,
|
||||
orInit(currentPeerId),
|
||||
ValueModel.fromRaw(operand),
|
||||
exportTo
|
||||
)
|
||||
case FlattenTag(operand, assignTo) =>
|
||||
leaf(ApRes(ValueModel.fromRaw(operand), Call.Export(assignTo, operand.`type`)))
|
||||
case CallServiceTag(serviceId, funcName, Call(args, exportTo)) =>
|
||||
leaf(
|
||||
CallServiceRes(
|
||||
serviceId,
|
||||
ValueModel.fromRaw(serviceId),
|
||||
funcName,
|
||||
CallRes(args, exportTo.headOption),
|
||||
currentPeerId
|
||||
.getOrElse(LiteralModel.initPeerId)
|
||||
CallRes(args.map(ValueModel.fromRaw), exportTo.headOption),
|
||||
orInit(currentPeerId)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package aqua.model.transform.res
|
||||
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.{ValueModel, VarModel}
|
||||
import aqua.raw.ops.Call
|
||||
|
||||
// TODO docs to all traits and objects
|
||||
sealed trait ResolvedOp
|
||||
@ -50,6 +50,4 @@ case class ApRes(operand: ValueModel, exportTo: Call.Export) extends ResolvedOp
|
||||
ApRes(f(operand), exportTo)
|
||||
|
||||
def mapExport(f: String => String): ApRes = copy(exportTo = exportTo.mapName(f))
|
||||
|
||||
def argVarNames: Set[String] = ValueModel.varName(operand).toSet
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package aqua.model.transform.res
|
||||
|
||||
import aqua.model.{LiteralModel, ServiceModel}
|
||||
import aqua.raw.ServiceRaw
|
||||
import aqua.raw.value.LiteralRaw
|
||||
import aqua.types.{ArrowType, ScalarType}
|
||||
|
||||
// TODO: docs
|
||||
@ -8,12 +9,12 @@ case class ServiceRes(name: String, members: List[(String, ArrowType)], defaultI
|
||||
|
||||
object ServiceRes {
|
||||
|
||||
def fromModel(sm: ServiceModel): ServiceRes =
|
||||
def fromModel(sm: ServiceRaw): ServiceRes =
|
||||
ServiceRes(
|
||||
name = sm.name,
|
||||
members = sm.arrows.toNel.toList,
|
||||
defaultId = sm.defaultId.collect {
|
||||
case LiteralModel(value, t) if ScalarType.string.acceptsValueOf(t) =>
|
||||
case LiteralRaw(value, t) if ScalarType.string.acceptsValueOf(t) =>
|
||||
value
|
||||
}
|
||||
)
|
||||
|
@ -1,7 +1,8 @@
|
||||
package aqua.model.transform.topology
|
||||
|
||||
import aqua.model.ValueModel
|
||||
import aqua.model.func.raw.{OnTag, ParGroupTag}
|
||||
import aqua.raw.ops.OnTag
|
||||
import aqua.raw.ops.OnTag
|
||||
import aqua.raw.value.ValueRaw
|
||||
import cats.data.Chain
|
||||
import cats.data.Chain.{:==, ==:, nil}
|
||||
import scribe.Logging
|
||||
@ -16,7 +17,7 @@ object PathFinder extends Logging {
|
||||
* @param toOn Next location
|
||||
* @return Chain of peers to visit in between
|
||||
*/
|
||||
def findPath(fromOn: List[OnTag], toOn: List[OnTag]): Chain[ValueModel] =
|
||||
def findPath(fromOn: List[OnTag], toOn: List[OnTag]): Chain[ValueRaw] =
|
||||
findPath(
|
||||
Chain.fromSeq(fromOn).reverse,
|
||||
Chain.fromSeq(toOn).reverse,
|
||||
@ -27,9 +28,9 @@ object PathFinder extends Logging {
|
||||
def findPath(
|
||||
fromOn: Chain[OnTag],
|
||||
toOn: Chain[OnTag],
|
||||
fromPeer: Option[ValueModel],
|
||||
toPeer: Option[ValueModel]
|
||||
): Chain[ValueModel] = {
|
||||
fromPeer: Option[ValueRaw],
|
||||
toPeer: Option[ValueRaw]
|
||||
): Chain[ValueRaw] = {
|
||||
logger.trace(s"FROM ON: $fromOn")
|
||||
logger.trace(s"TO ON: $toOn")
|
||||
|
||||
@ -64,12 +65,12 @@ object PathFinder extends Logging {
|
||||
* @return optimal path with no duplicates
|
||||
*/
|
||||
def optimizePath(
|
||||
peerIds: Chain[ValueModel],
|
||||
prefix: Chain[ValueModel],
|
||||
suffix: Chain[ValueModel]
|
||||
): Chain[ValueModel] = {
|
||||
peerIds: Chain[ValueRaw],
|
||||
prefix: Chain[ValueRaw],
|
||||
suffix: Chain[ValueRaw]
|
||||
): Chain[ValueRaw] = {
|
||||
val optimized = peerIds
|
||||
.foldLeft(Chain.empty[ValueModel]) {
|
||||
.foldLeft(Chain.empty[ValueRaw]) {
|
||||
case (acc, p) if acc.lastOption.contains(p) => acc
|
||||
case (acc, p) if acc.contains(p) => acc.takeWhile(_ != p) :+ p
|
||||
case (acc, p) => acc :+ p
|
||||
|
@ -1,11 +1,12 @@
|
||||
package aqua.model.transform.topology
|
||||
|
||||
import aqua.model.ValueModel
|
||||
import aqua.model.func.raw.*
|
||||
import aqua.model.func.raw.FuncOp.Tree
|
||||
import aqua.raw.ops.*
|
||||
import aqua.raw.ops.FuncOp.Tree
|
||||
import cats.Eval
|
||||
import cats.data.{Chain, NonEmptyList, OptionT}
|
||||
import aqua.model.transform.cursor.*
|
||||
import aqua.raw.ops
|
||||
import aqua.raw.ops.{FuncOp, GroupTag, NoExecTag, RawTag}
|
||||
import cats.syntax.traverse.*
|
||||
import cats.free.Cofree
|
||||
import scribe.Logging
|
||||
|
@ -1,10 +1,11 @@
|
||||
package aqua.model.transform.topology
|
||||
|
||||
import aqua.model.ValueModel.varName
|
||||
import aqua.model.transform.cursor.ChainZipper
|
||||
import aqua.model.func.raw.*
|
||||
import aqua.raw.ops.*
|
||||
import aqua.model.transform.res.*
|
||||
import aqua.model.{LiteralModel, ValueModel, VarModel}
|
||||
import aqua.raw.ops.{CallServiceTag, ForTag, NextTag, OnTag, ParTag, RawTag, SeqGroupTag, XorTag}
|
||||
import aqua.raw.value.ValueRaw
|
||||
import aqua.types.{BoxType, ScalarType}
|
||||
import cats.Eval
|
||||
import cats.data.Chain.{==:, nil}
|
||||
@ -67,7 +68,7 @@ case class Topology private (
|
||||
.getOrElse(Eval.now(None))
|
||||
}).memoize
|
||||
|
||||
lazy val currentPeerId: Option[ValueModel] = pathOn.value.headOption.map(_.peerId)
|
||||
lazy val currentPeerId: Option[ValueRaw] = pathOn.value.headOption.map(_.peerId)
|
||||
|
||||
lazy val prevSibling: Option[Topology] = cursor.toPrevSibling.map(_.topology)
|
||||
|
||||
@ -112,9 +113,9 @@ case class Topology private (
|
||||
// Where we finally are, after exit enforcement is applied
|
||||
lazy val finallyOn: Eval[List[OnTag]] = after.finallyOn(this).memoize
|
||||
|
||||
lazy val pathBefore: Eval[Chain[ValueModel]] = begins.pathBefore(this).memoize
|
||||
lazy val pathBefore: Eval[Chain[ValueRaw]] = begins.pathBefore(this).memoize
|
||||
|
||||
lazy val pathAfter: Eval[Chain[ValueModel]] = after.pathAfter(this).memoize
|
||||
lazy val pathAfter: Eval[Chain[ValueRaw]] = after.pathAfter(this).memoize
|
||||
}
|
||||
|
||||
object Topology extends Logging {
|
||||
@ -122,7 +123,7 @@ object Topology extends Logging {
|
||||
type Res = Cofree[Chain, ResolvedOp]
|
||||
|
||||
// Returns a peerId to go to in case it equals the last relay: useful when we do execution on the relay
|
||||
private def findRelayPathEnforcement(bef: List[OnTag], beg: List[OnTag]): Chain[ValueModel] =
|
||||
private def findRelayPathEnforcement(bef: List[OnTag], beg: List[OnTag]): Chain[ValueRaw] =
|
||||
Chain.fromOption(
|
||||
beg.headOption
|
||||
.map(_.peerId)
|
||||
@ -143,7 +144,7 @@ object Topology extends Logging {
|
||||
|
||||
def beginsOn(current: Topology): Eval[List[OnTag]] = current.pathOn
|
||||
|
||||
def pathBefore(current: Topology): Eval[Chain[ValueModel]] =
|
||||
def pathBefore(current: Topology): Eval[Chain[ValueRaw]] =
|
||||
(current.beforeOn, current.beginsOn).mapN { case (bef, beg) =>
|
||||
(PathFinder.findPath(bef, beg), bef, beg)
|
||||
}.flatMap { case (pb, bef, beg) =>
|
||||
@ -191,7 +192,7 @@ object Topology extends Logging {
|
||||
|
||||
// If exit is forced, make a path outside this node
|
||||
// – from where it ends to where execution is expected to continue
|
||||
def pathAfter(current: Topology): Eval[Chain[ValueModel]] =
|
||||
def pathAfter(current: Topology): Eval[Chain[ValueRaw]] =
|
||||
current.forceExit.flatMap {
|
||||
case true =>
|
||||
(current.endsOn, current.afterOn).mapN(PathFinder.findPath)
|
||||
@ -250,13 +251,13 @@ object Topology extends Logging {
|
||||
override def afterOn(current: Topology): Eval[List[OnTag]] =
|
||||
afterParent(current)
|
||||
|
||||
override def pathAfter(current: Topology): Eval[Chain[ValueModel]] =
|
||||
override def pathAfter(current: Topology): Eval[Chain[ValueRaw]] =
|
||||
current.forceExit
|
||||
.flatMap[Chain[ValueModel]] {
|
||||
case false => Eval.now(Chain.empty[ValueModel])
|
||||
.flatMap[Chain[ValueRaw]] {
|
||||
case false => Eval.now(Chain.empty[ValueRaw])
|
||||
case true =>
|
||||
(current.endsOn, current.afterOn, current.lastExecutesOn).mapN {
|
||||
case (e, a, _) if e == a => Chain.empty[ValueModel]
|
||||
case (e, a, _) if e == a => Chain.empty[ValueRaw]
|
||||
case (e, a, l) if l.contains(e) =>
|
||||
// Pingback in case no relays involved
|
||||
Chain.fromOption(a.headOption.map(_.peerId))
|
||||
@ -334,10 +335,10 @@ object Topology extends Logging {
|
||||
b.map(
|
||||
_.reverse
|
||||
.foldLeft((true, List.empty[OnTag])) {
|
||||
case ((true, acc), OnTag(_, r)) if r.exists(ValueModel.varName(_).contains(f.item)) =>
|
||||
case ((true, acc), OnTag(_, r)) if r.exists(_.usesVarNames.contains(f.item)) =>
|
||||
(false, acc)
|
||||
case ((true, acc @ (OnTag(_, r @ (r0 ==: _)) :: _)), OnTag(p, _))
|
||||
if ValueModel.varName(p).contains(f.item) =>
|
||||
if p.usesVarNames.contains(f.item) =>
|
||||
// This is to take the outstanding relay and force moving there
|
||||
(false, OnTag(r0, r) :: acc)
|
||||
case ((true, acc), on) => (true, on :: acc)
|
||||
@ -508,7 +509,7 @@ object Topology extends Logging {
|
||||
// Walks through peer IDs, doing a noop function on each
|
||||
// If same IDs are found in a row, does noop only once
|
||||
// if there's a chain like a -> b -> c -> ... -> b -> g, remove everything between b and b
|
||||
def through(peerIds: Chain[ValueModel], reversed: Boolean = false): Chain[Res] =
|
||||
def through(peerIds: Chain[ValueRaw], reversed: Boolean = false): Chain[Res] =
|
||||
peerIds.map { v =>
|
||||
v.lastType match {
|
||||
case _: BoxType =>
|
||||
@ -516,20 +517,20 @@ object Topology extends Logging {
|
||||
|
||||
MakeRes.fold(
|
||||
itemName,
|
||||
v,
|
||||
ValueModel.fromRaw(v),
|
||||
if (reversed)
|
||||
MakeRes.seq(
|
||||
MakeRes.next(itemName),
|
||||
MakeRes.noop(VarModel(itemName, ScalarType.string))
|
||||
MakeRes.noop(VarModel(itemName, ScalarType.string, Chain.empty))
|
||||
)
|
||||
else
|
||||
MakeRes.seq(
|
||||
MakeRes.noop(VarModel(itemName, ScalarType.string)),
|
||||
MakeRes.noop(VarModel(itemName, ScalarType.string, Chain.empty)),
|
||||
MakeRes.next(itemName)
|
||||
)
|
||||
)
|
||||
case _ =>
|
||||
MakeRes.noop(v)
|
||||
MakeRes.noop(ValueModel.fromRaw(v))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,19 +34,11 @@ case class IntoIndex[F[_]: Comonad](idx: F[Int]) extends LambdaOp[F] {
|
||||
def value: Int = idx.extract
|
||||
}
|
||||
|
||||
case class IntoArray[F[_]: Functor](override val unit: F[Unit]) extends LambdaOp[F] {
|
||||
override def as[T](v: T): F[T] = unit.as(v)
|
||||
|
||||
override def mapK[K[_]: Comonad](fk: F ~> K): IntoArray[K] = copy(fk(unit))
|
||||
}
|
||||
|
||||
object LambdaOp {
|
||||
|
||||
private val parseField: P[LambdaOp[Span.S]] =
|
||||
(`.` *> `name`).lift.map(IntoField(_))
|
||||
|
||||
private val parseArr: P[LambdaOp[Span.S]] = `*`.lift.map(IntoArray(_))
|
||||
|
||||
private val nonNegativeIntP0: P0[Int] =
|
||||
Numbers.nonNegativeIntString.map(_.toInt).?.map(_.getOrElse(0))
|
||||
|
||||
@ -54,7 +46,7 @@ object LambdaOp {
|
||||
(exclamation *> nonNegativeIntP0).lift.map(IntoIndex(_))
|
||||
|
||||
private val parseOp: P[LambdaOp[Span.S]] =
|
||||
P.oneOf(parseField.backtrack :: parseArr :: parseIdx :: Nil)
|
||||
P.oneOf(parseField.backtrack :: parseIdx :: Nil)
|
||||
|
||||
val ops: P[NonEmptyList[LambdaOp[Span.S]]] =
|
||||
parseOp.rep
|
||||
|
@ -16,9 +16,7 @@ class LambdaOpSpec extends AnyFlatSpec with Matchers with EitherValues {
|
||||
|
||||
opsP(".field") should be(NonEmptyList.of(IntoField[Id]("field")))
|
||||
opsP(".field.sub") should be(NonEmptyList.of(IntoField[Id]("field"), IntoField[Id]("sub")))
|
||||
opsP(".field*.sub") should be(
|
||||
NonEmptyList.of(IntoField[Id]("field"), IntoArray[Id](()), IntoField[Id]("sub"))
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
package aqua.semantics
|
||||
|
||||
import aqua.model.{AquaContext, EmptyModel, Model, VarModel}
|
||||
import aqua.raw.{AquaContext, Raw}
|
||||
import aqua.semantics.rules.abilities.AbilitiesState
|
||||
import aqua.semantics.rules.names.NamesState
|
||||
import aqua.semantics.rules.types.TypesState
|
||||
import cats.data.{Chain, State}
|
||||
import cats.kernel.Monoid
|
||||
import cats.syntax.monoid._
|
||||
import cats.syntax.monoid.*
|
||||
|
||||
case class CompilerState[S[_]](
|
||||
errors: Chain[SemanticError[S]] = Chain.empty[SemanticError[S]],
|
||||
@ -16,7 +16,7 @@ case class CompilerState[S[_]](
|
||||
)
|
||||
|
||||
object CompilerState {
|
||||
type St[S[_]] = State[CompilerState[S], Model]
|
||||
type St[S[_]] = State[CompilerState[S], Raw]
|
||||
|
||||
def init[F[_]](ctx: AquaContext): CompilerState[F] =
|
||||
CompilerState(
|
||||
@ -26,7 +26,7 @@ object CompilerState {
|
||||
)
|
||||
|
||||
implicit def compilerStateMonoid[S[_]]: Monoid[St[S]] = new Monoid[St[S]] {
|
||||
override def empty: St[S] = State.pure(EmptyModel("compiler state monoid empty"))
|
||||
override def empty: St[S] = State.pure(Raw.Empty("compiler state monoid empty"))
|
||||
|
||||
override def combine(x: St[S], y: St[S]): St[S] = for {
|
||||
a <- x.get
|
||||
|
@ -1,6 +1,6 @@
|
||||
package aqua.semantics
|
||||
|
||||
import aqua.model.Model
|
||||
import aqua.raw.Raw
|
||||
import aqua.parser.Expr
|
||||
import aqua.parser.expr.*
|
||||
import aqua.parser.expr.func.*
|
||||
@ -21,7 +21,7 @@ object ExprSem {
|
||||
N: NamesAlgebra[S, G],
|
||||
T: TypesAlgebra[S, G],
|
||||
V: ValuesAlgebra[S, G]
|
||||
): Prog[G, Model] =
|
||||
): Prog[G, Raw] =
|
||||
expr match {
|
||||
case expr: AbilityIdExpr[S] => new AbilityIdSem(expr).program[G]
|
||||
case expr: AssignmentExpr[S] => new AssignmentSem(expr).program[G]
|
||||
|
@ -1,7 +1,7 @@
|
||||
package aqua.semantics
|
||||
|
||||
import aqua.model.func.raw.FuncOp
|
||||
import aqua.model.{AquaContext, EmptyModel, Model, ScriptModel}
|
||||
import aqua.raw.ops.FuncOp
|
||||
import aqua.raw.{AquaContext, ContextRaw, Raw}
|
||||
import aqua.parser.lexer.Token
|
||||
import aqua.parser.{Ast, Expr}
|
||||
import aqua.semantics.rules.abilities.{AbilitiesAlgebra, AbilitiesInterpreter, AbilitiesState}
|
||||
@ -28,21 +28,21 @@ object Semantics extends Logging {
|
||||
A: AbilitiesAlgebra[S, G],
|
||||
N: NamesAlgebra[S, G],
|
||||
T: TypesAlgebra[S, G]
|
||||
): (Expr[S], Chain[G[Model]]) => Eval[G[Model]] = { case (expr, inners) =>
|
||||
): (Expr[S], Chain[G[Raw]]) => Eval[G[Raw]] = { case (expr, inners) =>
|
||||
Eval later ExprSem
|
||||
.getProg[S, G](expr)
|
||||
.apply(
|
||||
// TODO instead of foldRight, do slidingWindow for 2 elements, merge right associative ones
|
||||
// Then foldLeft just like now
|
||||
inners
|
||||
.foldRight[G[List[Model]]](List.empty[Model].pure[G]) { case (a, b) =>
|
||||
.foldRight[G[List[Raw]]](List.empty[Raw].pure[G]) { case (a, b) =>
|
||||
(a, b).mapN {
|
||||
case (prev: FuncOp, (next: FuncOp) :: tail) if next.isRightAssoc =>
|
||||
(prev :+: next) :: tail
|
||||
case (prev, acc) => prev :: acc
|
||||
}
|
||||
}
|
||||
.map(_.reduceLeftOption(_ |+| _).getOrElse(Model.empty("AST is empty")))
|
||||
.map(_.reduceLeftOption(_ |+| _).getOrElse(Raw.empty("AST is empty")))
|
||||
)
|
||||
}
|
||||
|
||||
@ -53,7 +53,7 @@ object Semantics extends Logging {
|
||||
with NamesAlgebra[S, Interpreter[S, *]] with ValuesAlgebra[S, Interpreter[S, *]]
|
||||
with AbilitiesAlgebra[S, Interpreter[S, *]]
|
||||
|
||||
def transpile[S[_]](ast: Ast[S]): Interpreter[S, Model] = {
|
||||
def transpile[S[_]](ast: Ast[S]): Interpreter[S, Raw] = {
|
||||
import monocle.syntax.all.*
|
||||
|
||||
implicit val re: ReportError[S, CompilerState[S]] =
|
||||
@ -76,7 +76,7 @@ object Semantics extends Logging {
|
||||
ast.cata(folder[S, Interpreter[S, *]]).value
|
||||
}
|
||||
|
||||
private def astToState[S[_]](ast: Ast[S]): Interpreter[S, Model] =
|
||||
private def astToState[S[_]](ast: Ast[S]): Interpreter[S, Raw] =
|
||||
transpile[S](ast)
|
||||
|
||||
def process[S[_]](ast: Ast[S], init: AquaContext)(implicit
|
||||
@ -85,12 +85,12 @@ object Semantics extends Logging {
|
||||
astToState[S](ast)
|
||||
.run(CompilerState.init[S](init))
|
||||
.map {
|
||||
case (state, gen: ScriptModel) =>
|
||||
val ctx = AquaContext.fromScriptModel(gen, init)
|
||||
case (state, gen: ContextRaw) =>
|
||||
val ctx = AquaContext.fromRawContext(gen, init)
|
||||
NonEmptyChain
|
||||
.fromChain(state.errors)
|
||||
.fold[ValidatedNec[SemanticError[S], AquaContext]](Valid(ctx))(Invalid(_))
|
||||
case (state, _: EmptyModel) =>
|
||||
case (state, _: Raw.Empty) =>
|
||||
NonEmptyChain
|
||||
.fromChain(state.errors)
|
||||
.fold[ValidatedNec[SemanticError[S], AquaContext]](Valid(init))(Invalid(_))
|
||||
|
@ -1,19 +1,19 @@
|
||||
package aqua.semantics.expr
|
||||
|
||||
import aqua.model.{Model, TypeModel}
|
||||
import aqua.parser.expr.AliasExpr
|
||||
import aqua.raw.{Raw, TypeRaw}
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.types.TypesAlgebra
|
||||
import cats.syntax.functor._
|
||||
import cats.syntax.functor.*
|
||||
import cats.Monad
|
||||
import cats.Applicative
|
||||
import cats.syntax.flatMap._
|
||||
import cats.syntax.flatMap.*
|
||||
|
||||
class AliasSem[S[_]](val expr: AliasExpr[S]) extends AnyVal {
|
||||
|
||||
def program[Alg[_]: Monad](implicit T: TypesAlgebra[S, Alg]): Prog[Alg, Model] =
|
||||
def program[Alg[_]: Monad](implicit T: TypesAlgebra[S, Alg]): Prog[Alg, Raw] =
|
||||
T.resolveType(expr.target).flatMap {
|
||||
case Some(t) => T.defineAlias(expr.name, t) as (TypeModel(expr.name.value, t): Model)
|
||||
case None => Applicative[Alg].pure(Model.error("Alias type unresolved"))
|
||||
case Some(t) => T.defineAlias(expr.name, t) as (TypeRaw(expr.name.value, t): Raw)
|
||||
case None => Applicative[Alg].pure(Raw.error("Alias type unresolved"))
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
package aqua.semantics.expr
|
||||
|
||||
import aqua.model.{Model, TypeModel}
|
||||
import aqua.parser.expr.ArrowTypeExpr
|
||||
import aqua.raw.{Raw, TypeRaw}
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
||||
import aqua.semantics.rules.types.TypesAlgebra
|
||||
import cats.syntax.functor._
|
||||
import cats.syntax.applicative._
|
||||
import cats.syntax.flatMap._
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.Monad
|
||||
|
||||
class ArrowTypeSem[S[_]](val expr: ArrowTypeExpr[S]) extends AnyVal {
|
||||
@ -15,10 +15,10 @@ class ArrowTypeSem[S[_]](val expr: ArrowTypeExpr[S]) extends AnyVal {
|
||||
def program[Alg[_]: Monad](implicit
|
||||
T: TypesAlgebra[S, Alg],
|
||||
A: AbilitiesAlgebra[S, Alg]
|
||||
): Prog[Alg, Model] =
|
||||
): Prog[Alg, Raw] =
|
||||
T.resolveArrowDef(expr.`type`).flatMap {
|
||||
case Some(t) => A.defineArrow(expr.name, t) as (TypeModel(expr.name.value, t): Model)
|
||||
case None => Model.error("Arrow type unresolved").pure[Alg]
|
||||
case Some(t) => A.defineArrow(expr.name, t) as (TypeRaw(expr.name.value, t): Raw)
|
||||
case None => Raw.error("Arrow type unresolved").pure[Alg]
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
package aqua.semantics.expr
|
||||
|
||||
import aqua.model.{ConstantModel, Model}
|
||||
import aqua.parser.expr.ConstantExpr
|
||||
import aqua.raw.{ConstantRaw, Raw}
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.ValuesAlgebra
|
||||
import aqua.semantics.rules.names.NamesAlgebra
|
||||
import aqua.semantics.rules.types.TypesAlgebra
|
||||
import cats.syntax.functor._
|
||||
import cats.syntax.applicative._
|
||||
import cats.syntax.flatMap._
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.Monad
|
||||
|
||||
class ConstantSem[S[_]](val expr: ConstantExpr[S]) extends AnyVal {
|
||||
@ -17,7 +17,7 @@ class ConstantSem[S[_]](val expr: ConstantExpr[S]) extends AnyVal {
|
||||
V: ValuesAlgebra[S, Alg],
|
||||
N: NamesAlgebra[S, Alg],
|
||||
T: TypesAlgebra[S, Alg]
|
||||
): Prog[Alg, Model] = {
|
||||
): Prog[Alg, Raw] = {
|
||||
for {
|
||||
defined <- N.constantDefined(expr.name)
|
||||
v <- V.valueToModel(expr.value)
|
||||
@ -25,20 +25,20 @@ class ConstantSem[S[_]](val expr: ConstantExpr[S]) extends AnyVal {
|
||||
case (Some(definedType), Some((vm, actualType)), true) =>
|
||||
T.ensureTypeMatches(expr.value, definedType, actualType).map {
|
||||
case true =>
|
||||
Model.empty(s"Constant with name ${expr.name} was already defined, skipping")
|
||||
Raw.empty(s"Constant with name ${expr.name} was already defined, skipping")
|
||||
case false =>
|
||||
Model.error(s"Constant with name ${expr.name} was defined with different type")
|
||||
Raw.error(s"Constant with name ${expr.name} was defined with different type")
|
||||
}
|
||||
case (Some(_), _, _) =>
|
||||
Model.error(s"Name '${expr.name.value}' was already defined").pure[Alg]
|
||||
Raw.error(s"Name '${expr.name.value}' was already defined").pure[Alg]
|
||||
case (_, None, _) =>
|
||||
Model.error(s"There is no such variable ${expr.value}").pure[Alg]
|
||||
Raw.error(s"There is no such variable ${expr.value}").pure[Alg]
|
||||
case (_, Some(t), _) =>
|
||||
N.defineConstant(expr.name, t._2) as (ConstantModel(
|
||||
N.defineConstant(expr.name, t._2) as (ConstantRaw(
|
||||
expr.name.value,
|
||||
t._1,
|
||||
expr.skipIfAlreadyDefined
|
||||
): Model)
|
||||
): Raw)
|
||||
}
|
||||
} yield model
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
package aqua.semantics.expr
|
||||
|
||||
import aqua.model.{Model, TypeModel}
|
||||
import aqua.parser.expr.DataStructExpr
|
||||
import aqua.raw.{Raw, TypeRaw}
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.names.NamesAlgebra
|
||||
import aqua.semantics.rules.types.TypesAlgebra
|
||||
import aqua.types.StructType
|
||||
import cats.syntax.functor._
|
||||
import cats.syntax.applicative._
|
||||
import cats.syntax.flatMap._
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.Monad
|
||||
|
||||
class DataStructSem[S[_]](val expr: DataStructExpr[S]) extends AnyVal {
|
||||
@ -16,15 +16,15 @@ class DataStructSem[S[_]](val expr: DataStructExpr[S]) extends AnyVal {
|
||||
def program[Alg[_]: Monad](implicit
|
||||
N: NamesAlgebra[S, Alg],
|
||||
T: TypesAlgebra[S, Alg]
|
||||
): Prog[Alg, Model] =
|
||||
Prog.after((_: Model) =>
|
||||
): Prog[Alg, Raw] =
|
||||
Prog.after((_: Raw) =>
|
||||
T.purgeFields(expr.name).flatMap {
|
||||
case Some(fields) =>
|
||||
T.defineDataType(expr.name, fields) as (TypeModel(
|
||||
T.defineDataType(expr.name, fields) as (TypeRaw(
|
||||
expr.name.value,
|
||||
StructType(expr.name.value, fields)
|
||||
): Model)
|
||||
case None => Model.error("Data struct types unresolved").pure[Alg]
|
||||
): Raw)
|
||||
case None => Raw.error("Data struct types unresolved").pure[Alg]
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -1,20 +1,20 @@
|
||||
package aqua.semantics.expr
|
||||
|
||||
import aqua.model.{Model, TypeModel}
|
||||
import aqua.parser.expr.FieldTypeExpr
|
||||
import aqua.raw.{Raw, TypeRaw}
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.types.TypesAlgebra
|
||||
import cats.syntax.functor._
|
||||
import cats.syntax.applicative._
|
||||
import cats.syntax.flatMap._
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.Monad
|
||||
|
||||
class FieldTypeSem[S[_]](val expr: FieldTypeExpr[S]) extends AnyVal {
|
||||
|
||||
def program[Alg[_]: Monad](implicit T: TypesAlgebra[S, Alg]): Prog[Alg, Model] =
|
||||
def program[Alg[_]: Monad](implicit T: TypesAlgebra[S, Alg]): Prog[Alg, Raw] =
|
||||
T.resolveType(expr.`type`).flatMap {
|
||||
case Some(t) => T.defineField(expr.name, t) as (TypeModel(expr.name.value, t): Model)
|
||||
case None => Model.error("Field type unresolved").pure[Alg]
|
||||
case Some(t) => T.defineField(expr.name, t) as (TypeRaw(expr.name.value, t): Raw)
|
||||
case None => Raw.error("Field type unresolved").pure[Alg]
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,21 +1,21 @@
|
||||
package aqua.semantics.expr
|
||||
|
||||
import aqua.model.{Model, ScriptModel}
|
||||
import aqua.parser.expr.RootExpr
|
||||
import aqua.raw.{ContextRaw, Raw}
|
||||
import aqua.semantics.Prog
|
||||
import cats.syntax.applicative._
|
||||
import cats.syntax.applicative.*
|
||||
import cats.Monad
|
||||
|
||||
class RootSem[S[_]](val expr: RootExpr[S]) extends AnyVal {
|
||||
|
||||
def program[Alg[_]: Monad]: Prog[Alg, Model] =
|
||||
def program[Alg[_]: Monad]: Prog[Alg, Raw] =
|
||||
Prog.after {
|
||||
case sm: ScriptModel =>
|
||||
case sm: ContextRaw =>
|
||||
sm.pure[Alg]
|
||||
case m =>
|
||||
ScriptModel
|
||||
.toScriptPart(m)
|
||||
.getOrElse(Model.error("Root contains not a script model, it's " + m))
|
||||
ContextRaw
|
||||
.contextPart(m)
|
||||
// TODO .getOrElse(Model.error("Root contains not a script model, it's " + m))
|
||||
.pure[Alg]
|
||||
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
package aqua.semantics.expr
|
||||
|
||||
import aqua.model.{Model, ServiceModel, ValueModel}
|
||||
import aqua.parser.expr.ServiceExpr
|
||||
import aqua.raw.{Raw, ServiceRaw}
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.ValuesAlgebra
|
||||
import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
||||
import aqua.semantics.rules.names.NamesAlgebra
|
||||
import aqua.semantics.rules.types.TypesAlgebra
|
||||
import cats.syntax.apply._
|
||||
import cats.syntax.flatMap._
|
||||
import cats.syntax.functor._
|
||||
import cats.syntax.applicative._
|
||||
import cats.syntax.apply.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.applicative.*
|
||||
import cats.Monad
|
||||
|
||||
class ServiceSem[S[_]](val expr: ServiceExpr[S]) extends AnyVal {
|
||||
@ -20,10 +20,10 @@ class ServiceSem[S[_]](val expr: ServiceExpr[S]) extends AnyVal {
|
||||
N: NamesAlgebra[S, Alg],
|
||||
T: TypesAlgebra[S, Alg],
|
||||
V: ValuesAlgebra[S, Alg]
|
||||
): Prog[Alg, Model] =
|
||||
): Prog[Alg, Raw] =
|
||||
Prog.around(
|
||||
A.beginScope(expr.name),
|
||||
(_: Unit, body: Model) =>
|
||||
(_: Unit, body: Raw) =>
|
||||
(A.purgeArrows(expr.name) <* A.endScope()).flatMap {
|
||||
case Some(nel) =>
|
||||
val arrows = nel.map(kv => kv._1.value -> kv._2).toNem
|
||||
@ -44,11 +44,11 @@ class ServiceSem[S[_]](val expr: ServiceExpr[S]) extends AnyVal {
|
||||
)
|
||||
} yield
|
||||
if (defineResult) {
|
||||
ServiceModel(expr.name.value, arrows, defaultId)
|
||||
} else Model.empty("Service not created due to validation errors")
|
||||
ServiceRaw(expr.name.value, arrows, defaultId)
|
||||
} else Raw.empty("Service not created due to validation errors")
|
||||
|
||||
case None =>
|
||||
Model.error("Service has no arrows, fails").pure[Alg]
|
||||
Raw.error("Service has no arrows, fails").pure[Alg]
|
||||
|
||||
}
|
||||
)
|
||||
|
@ -1,7 +1,7 @@
|
||||
package aqua.semantics.expr.func
|
||||
|
||||
import aqua.model.Model
|
||||
import aqua.model.func.raw.{AbilityIdTag, FuncOp}
|
||||
import aqua.raw.Raw
|
||||
import aqua.raw.ops.{AbilityIdTag, FuncOp}
|
||||
import aqua.parser.expr.func.AbilityIdExpr
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.ValuesAlgebra
|
||||
@ -16,14 +16,14 @@ class AbilityIdSem[S[_]](val expr: AbilityIdExpr[S]) extends AnyVal {
|
||||
def program[Alg[_]: Monad](implicit
|
||||
A: AbilitiesAlgebra[S, Alg],
|
||||
V: ValuesAlgebra[S, Alg]
|
||||
): Prog[Alg, Model] =
|
||||
): Prog[Alg, Raw] =
|
||||
V.ensureIsString(expr.id) >> V.valueToModel(
|
||||
expr.id
|
||||
) >>= {
|
||||
case Some(id) =>
|
||||
A.setServiceId(expr.ability, expr.id, id) as (FuncOp.leaf(
|
||||
AbilityIdTag(id, expr.ability.value)
|
||||
): Model)
|
||||
case _ => Model.error("Cannot resolve ability ID").pure[Alg]
|
||||
): Raw)
|
||||
case _ => Raw.error("Cannot resolve ability ID").pure[Alg]
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
package aqua.semantics.expr.func
|
||||
|
||||
import aqua.model.func.ArrowModel
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.func.raw.{FuncOp, FuncOps, RestrictionTag, ReturnTag, SeqTag}
|
||||
import aqua.model.{Model, ValueModel, VarModel}
|
||||
import aqua.raw.ops.{Call, FuncOp, FuncOps, RestrictionTag, ReturnTag, SeqTag}
|
||||
import aqua.parser.expr.FuncExpr
|
||||
import aqua.parser.expr.func.ArrowExpr
|
||||
import aqua.parser.lexer.{Arg, DataTypeToken}
|
||||
import aqua.raw.Raw
|
||||
import aqua.raw.arrow.ArrowRaw
|
||||
import aqua.raw.value.VarRaw
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.ValuesAlgebra
|
||||
import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
||||
@ -52,11 +52,11 @@ class ArrowSem[S[_]](val expr: ArrowExpr[S]) extends AnyVal {
|
||||
.as(arrowType)
|
||||
)
|
||||
|
||||
def after[Alg[_]: Monad](funcArrow: ArrowType, bodyGen: Model)(implicit
|
||||
def after[Alg[_]: Monad](funcArrow: ArrowType, bodyGen: Raw)(implicit
|
||||
T: TypesAlgebra[S, Alg],
|
||||
N: NamesAlgebra[S, Alg],
|
||||
A: AbilitiesAlgebra[S, Alg]
|
||||
): Alg[Model] =
|
||||
): Alg[Raw] =
|
||||
A.endScope() *> (N.streamsDefinedWithinScope(), T.endArrowScope(expr.arrowTypeExpr)).mapN {
|
||||
(streams, retValues) =>
|
||||
bodyGen match {
|
||||
@ -66,8 +66,8 @@ class ArrowSem[S[_]](val expr: ArrowExpr[S]) extends AnyVal {
|
||||
// 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)
|
||||
case (VarRaw(n, StreamType(_), _), StreamType(_)) => n -> None
|
||||
case (VarRaw(n, StreamType(_), _), t) => n -> Some(t)
|
||||
}.toMap
|
||||
|
||||
val builtStreams = retStreams.collect { case (n, Some(t)) =>
|
||||
@ -84,14 +84,14 @@ class ArrowSem[S[_]](val expr: ArrowExpr[S]) extends AnyVal {
|
||||
val (body, retValuesFix) = localStreams.foldLeft((m, retValues)) { case ((b, rs), n) =>
|
||||
if (
|
||||
rs.exists {
|
||||
case VarModel(`n`, _, _) => true
|
||||
case VarRaw(`n`, _, _) => true
|
||||
case _ => false
|
||||
}
|
||||
)
|
||||
FuncOp.wrap(
|
||||
RestrictionTag(n, isStream = true),
|
||||
FuncOps.seq(
|
||||
b :: rs.collect { case vn @ VarModel(`n`, _, _) =>
|
||||
b :: rs.collect { case vn @ VarRaw(`n`, _, _) =>
|
||||
FuncOps.canonicalize(
|
||||
vn,
|
||||
Call.Export(s"$n-fix", builtStreams.getOrElse(n, vn.lastType))
|
||||
@ -99,14 +99,14 @@ class ArrowSem[S[_]](val expr: ArrowExpr[S]) extends AnyVal {
|
||||
}: _*
|
||||
)
|
||||
) -> rs.map {
|
||||
case vn @ VarModel(`n`, _, _) =>
|
||||
VarModel(s"$n-fix", builtStreams.getOrElse(n, vn.lastType))
|
||||
case vn @ VarRaw(`n`, _, _) =>
|
||||
VarRaw(s"$n-fix", builtStreams.getOrElse(n, vn.lastType))
|
||||
case vm => vm
|
||||
}
|
||||
else FuncOp.wrap(RestrictionTag(n, isStream = true), b) -> rs
|
||||
}
|
||||
|
||||
ArrowModel(funcArrow, retValuesFix, body)
|
||||
ArrowRaw(funcArrow, retValuesFix, body)
|
||||
case m =>
|
||||
m
|
||||
}
|
||||
@ -116,7 +116,7 @@ class ArrowSem[S[_]](val expr: ArrowExpr[S]) extends AnyVal {
|
||||
T: TypesAlgebra[S, Alg],
|
||||
N: NamesAlgebra[S, Alg],
|
||||
A: AbilitiesAlgebra[S, Alg]
|
||||
): Prog[Alg, Model] =
|
||||
): Prog[Alg, Raw] =
|
||||
Prog.around(
|
||||
before[Alg],
|
||||
after[Alg]
|
||||
|
@ -1,7 +1,7 @@
|
||||
package aqua.semantics.expr.func
|
||||
|
||||
import aqua.model.Model
|
||||
import aqua.model.func.raw.{AssignmentTag, FuncOp}
|
||||
import aqua.raw.Raw
|
||||
import aqua.raw.ops.{AssignmentTag, FuncOp}
|
||||
import aqua.parser.expr.func.AssignmentExpr
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.ValuesAlgebra
|
||||
@ -16,12 +16,12 @@ class AssignmentSem[S[_]](val expr: AssignmentExpr[S]) extends AnyVal {
|
||||
def program[Alg[_]: Monad](implicit
|
||||
N: NamesAlgebra[S, Alg],
|
||||
V: ValuesAlgebra[S, Alg]
|
||||
): Prog[Alg, Model] =
|
||||
): Prog[Alg, Raw] =
|
||||
V.valueToModel(expr.value).flatMap {
|
||||
case Some(vm) =>
|
||||
N.define(expr.variable, vm.lastType) as (FuncOp
|
||||
.leaf(AssignmentTag(vm, expr.variable.value)): Model)
|
||||
case _ => Model.error("Cannot resolve assignment type").pure[Alg]
|
||||
.leaf(AssignmentTag(vm, expr.variable.value)): Raw)
|
||||
case _ => Raw.error("Cannot resolve assignment type").pure[Alg]
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
package aqua.semantics.expr.func
|
||||
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.func.raw.{CallArrowTag, CallServiceTag, FuncOp}
|
||||
import aqua.model.{Model, ValueModel}
|
||||
import aqua.raw.ops.Call
|
||||
import aqua.raw.ops.{CallArrowTag, CallServiceTag, FuncOp}
|
||||
import aqua.raw.Raw
|
||||
import aqua.parser.expr.func.CallArrowExpr
|
||||
import aqua.raw.value.ValueRaw
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.ValuesAlgebra
|
||||
import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
||||
@ -29,7 +30,7 @@ class CallArrowSem[S[_]](val expr: CallArrowExpr[S]) extends AnyVal {
|
||||
N: NamesAlgebra[S, Alg],
|
||||
T: TypesAlgebra[S, Alg],
|
||||
V: ValuesAlgebra[S, Alg]
|
||||
): Alg[(List[ValueModel], List[Type])] =
|
||||
): Alg[(List[ValueRaw], List[Type])] =
|
||||
V.checkArguments(expr.funcName, at, args) >> variables
|
||||
.foldLeft(algUnit[Alg].as((List.empty[Type], at.codomain.toList)))((f, exportVar) =>
|
||||
f.flatMap {
|
||||
@ -71,7 +72,7 @@ class CallArrowSem[S[_]](val expr: CallArrowExpr[S]) extends AnyVal {
|
||||
}.traverse(identity))
|
||||
}
|
||||
|
||||
def callServiceTag[Alg[_]: Monad](arrowType: ArrowType, serviceId: Option[ValueModel])(implicit
|
||||
def callServiceTag[Alg[_]: Monad](arrowType: ArrowType, serviceId: Option[ValueRaw])(implicit
|
||||
N: NamesAlgebra[S, Alg],
|
||||
A: AbilitiesAlgebra[S, Alg],
|
||||
T: TypesAlgebra[S, Alg],
|
||||
@ -109,7 +110,7 @@ class CallArrowSem[S[_]](val expr: CallArrowExpr[S]) extends AnyVal {
|
||||
A: AbilitiesAlgebra[S, Alg],
|
||||
T: TypesAlgebra[S, Alg],
|
||||
V: ValuesAlgebra[S, Alg]
|
||||
): Prog[Alg, Model] =
|
||||
toModel[Alg].map(_.getOrElse(Model.error("CallArrow can't be converted to Model")))
|
||||
): Prog[Alg, Raw] =
|
||||
toModel[Alg].map(_.getOrElse(Raw.error("CallArrow can't be converted to Model")))
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
package aqua.semantics.expr.func
|
||||
|
||||
import aqua.model.func.raw.{AssignmentTag, FuncOp, FuncOps, XorTag}
|
||||
import aqua.model.{Model, VarModel}
|
||||
import aqua.raw.ops.{AssignmentTag, FuncOp, FuncOps, XorTag}
|
||||
import aqua.parser.expr.func.CatchExpr
|
||||
import aqua.raw.value.ValueRaw
|
||||
import aqua.raw.Raw
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
||||
import aqua.semantics.rules.names.NamesAlgebra
|
||||
@ -16,21 +17,21 @@ class CatchSem[S[_]](val expr: CatchExpr[S]) extends AnyVal {
|
||||
def program[Alg[_]: Monad](implicit
|
||||
N: NamesAlgebra[S, Alg],
|
||||
A: AbilitiesAlgebra[S, Alg]
|
||||
): Prog[Alg, Model] =
|
||||
): Prog[Alg, Raw] =
|
||||
Prog
|
||||
.around(
|
||||
N.beginScope(expr.name) >>
|
||||
N.define(expr.name, VarModel.lastError.`type`),
|
||||
(_: Boolean, g: Model) =>
|
||||
N.define(expr.name, ValueRaw.LastError.`type`),
|
||||
(_: Boolean, g: Raw) =>
|
||||
g match {
|
||||
case op: FuncOp =>
|
||||
N.endScope() as (FuncOp.wrap(
|
||||
XorTag,
|
||||
FuncOps.seq(
|
||||
FuncOp.leaf(AssignmentTag(VarModel.lastError, expr.name.value)),
|
||||
FuncOp.leaf(AssignmentTag(ValueRaw.LastError, expr.name.value)),
|
||||
op
|
||||
)
|
||||
): Model)
|
||||
): Raw)
|
||||
case _ =>
|
||||
N.endScope() as g
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
package aqua.semantics.expr.func
|
||||
|
||||
import aqua.model.Model
|
||||
import aqua.model.func.raw.{ClosureTag, FuncOp}
|
||||
import aqua.model.func.{ArrowModel, FuncModel}
|
||||
import aqua.raw.Raw
|
||||
import aqua.raw.ops.{ClosureTag, FuncOp}
|
||||
import aqua.raw.arrow.{ArrowRaw, FuncRaw}
|
||||
import aqua.parser.expr.FuncExpr
|
||||
import aqua.parser.expr.func.ClosureExpr
|
||||
import aqua.parser.lexer.Arg
|
||||
@ -18,17 +18,17 @@ class ClosureSem[S[_]](val expr: ClosureExpr[S]) extends AnyVal {
|
||||
|
||||
def program[Alg[_]: Monad](implicit
|
||||
N: NamesAlgebra[S, Alg]
|
||||
): Prog[Alg, Model] =
|
||||
): Prog[Alg, Raw] =
|
||||
Prog.after {
|
||||
case arrow: ArrowModel =>
|
||||
case arrow: ArrowRaw =>
|
||||
N.defineArrow(
|
||||
expr.name,
|
||||
arrow.`type`,
|
||||
isRoot = false
|
||||
) as FuncOp.leaf(ClosureTag(FuncModel(expr.name.value, arrow)))
|
||||
) as FuncOp.leaf(ClosureTag(FuncRaw(expr.name.value, arrow)))
|
||||
|
||||
case m =>
|
||||
Model.error("Closure must continue with an arrow definition").pure[Alg]
|
||||
Raw.error("Closure must continue with an arrow definition").pure[Alg]
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
package aqua.semantics.expr.func
|
||||
|
||||
import aqua.model.Model
|
||||
import aqua.model.func.raw.{FuncOp, ParTag}
|
||||
import aqua.raw.ops.{FuncOp, ParTag}
|
||||
import aqua.parser.expr.func.CoExpr
|
||||
import aqua.raw.Raw
|
||||
import aqua.semantics.Prog
|
||||
import cats.syntax.applicative._
|
||||
import cats.syntax.applicative.*
|
||||
import cats.Monad
|
||||
|
||||
class CoSem[S[_]](val expr: CoExpr[S]) extends AnyVal {
|
||||
|
||||
def program[Alg[_]: Monad]: Prog[Alg, Model] =
|
||||
Prog.after[Alg, Model] {
|
||||
def program[Alg[_]: Monad]: Prog[Alg, Raw] =
|
||||
Prog.after[Alg, Raw] {
|
||||
case g: FuncOp =>
|
||||
FuncOp.wrap(ParTag.Detach, g).pure[Alg]
|
||||
case g => g.pure[Alg]
|
||||
|
@ -1,8 +1,9 @@
|
||||
package aqua.semantics.expr.func
|
||||
|
||||
import aqua.model.func.raw.{DeclareStreamTag, FuncOp}
|
||||
import aqua.model.{Model, VarModel}
|
||||
import aqua.raw.ops.{DeclareStreamTag, FuncOp}
|
||||
import aqua.parser.expr.func.DeclareStreamExpr
|
||||
import aqua.raw.Raw
|
||||
import aqua.raw.value.VarRaw
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.names.NamesAlgebra
|
||||
import aqua.semantics.rules.types.TypesAlgebra
|
||||
@ -18,7 +19,7 @@ class DeclareStreamSem[S[_]](val expr: DeclareStreamExpr[S]) {
|
||||
def program[Alg[_]: Monad](implicit
|
||||
N: NamesAlgebra[S, Alg],
|
||||
T: TypesAlgebra[S, Alg]
|
||||
): Prog[Alg, Model] =
|
||||
): Prog[Alg, Raw] =
|
||||
Prog.leaf(
|
||||
T.resolveType(expr.`type`)
|
||||
.flatMap {
|
||||
@ -38,9 +39,9 @@ class DeclareStreamSem[S[_]](val expr: DeclareStreamExpr[S]) {
|
||||
}
|
||||
.map {
|
||||
case Some(streamType) =>
|
||||
val valueModel = VarModel(expr.name.value, streamType, Chain.empty)
|
||||
FuncOp.leaf(DeclareStreamTag(valueModel)): Model
|
||||
case None => Model.error(s"Name `${expr.name.value}` not defined")
|
||||
val valueModel = VarRaw(expr.name.value, streamType)
|
||||
FuncOp.leaf(DeclareStreamTag(valueModel)): Raw
|
||||
case None => Raw.error(s"Name `${expr.name.value}` not defined")
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -1,18 +1,18 @@
|
||||
package aqua.semantics.expr.func
|
||||
|
||||
import aqua.model.Model
|
||||
import aqua.model.func.raw.{FuncOp, XorTag}
|
||||
import aqua.raw.ops.{FuncOp, XorTag}
|
||||
import aqua.parser.expr.func.ElseOtherwiseExpr
|
||||
import aqua.raw.Raw
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
||||
import cats.syntax.applicative._
|
||||
import cats.syntax.applicative.*
|
||||
import cats.Monad
|
||||
|
||||
class ElseOtherwiseSem[S[_]](val expr: ElseOtherwiseExpr[S]) extends AnyVal {
|
||||
|
||||
def program[Alg[_]: Monad](implicit A: AbilitiesAlgebra[S, Alg]): Prog[Alg, Model] =
|
||||
def program[Alg[_]: Monad](implicit A: AbilitiesAlgebra[S, Alg]): Prog[Alg, Raw] =
|
||||
Prog
|
||||
.after[Alg, Model] {
|
||||
.after[Alg, Raw] {
|
||||
case g: FuncOp => FuncOp.wrap(XorTag, g).pure[Alg]
|
||||
case g => g.pure[Alg]
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
package aqua.semantics.expr.func
|
||||
|
||||
import aqua.model.func.raw.*
|
||||
import aqua.model.{Model, ValueModel}
|
||||
import aqua.raw.Raw
|
||||
import aqua.parser.expr.func.ForExpr
|
||||
import aqua.raw.value.ValueRaw
|
||||
import aqua.raw.ops.*
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.ValuesAlgebra
|
||||
import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
||||
@ -23,21 +24,21 @@ class ForSem[S[_]](val expr: ForExpr[S]) extends AnyVal {
|
||||
N: NamesAlgebra[S, F],
|
||||
T: TypesAlgebra[S, F],
|
||||
A: AbilitiesAlgebra[S, F]
|
||||
): Prog[F, Model] =
|
||||
): Prog[F, Raw] =
|
||||
Prog
|
||||
.around(
|
||||
N.beginScope(expr.item) >> V.valueToModel(expr.iterable).flatMap[Option[ValueModel]] {
|
||||
N.beginScope(expr.item) >> V.valueToModel(expr.iterable).flatMap[Option[ValueRaw]] {
|
||||
case Some(vm) =>
|
||||
vm.lastType match {
|
||||
case t: BoxType =>
|
||||
N.define(expr.item, t.element).as(Option(vm))
|
||||
case dt =>
|
||||
T.ensureTypeMatches(expr.iterable, ArrayType(dt), dt).as(Option.empty[ValueModel])
|
||||
T.ensureTypeMatches(expr.iterable, ArrayType(dt), dt).as(Option.empty[ValueRaw])
|
||||
}
|
||||
|
||||
case _ => None.pure[F]
|
||||
},
|
||||
(stOpt: Option[ValueModel], ops: Model) =>
|
||||
(stOpt: Option[ValueRaw], ops: Raw) =>
|
||||
N.streamsDefinedWithinScope()
|
||||
.map((streams: Set[String]) =>
|
||||
(stOpt, ops) match {
|
||||
@ -70,7 +71,7 @@ class ForSem[S[_]](val expr: ForExpr[S]) extends AnyVal {
|
||||
if (innerTag == ParTag) FuncOp.wrap(ParTag.Detach, forTag)
|
||||
else forTag
|
||||
case _ =>
|
||||
Model.error("Wrong body of the For expression")
|
||||
Raw.error("Wrong body of the For expression")
|
||||
}
|
||||
) <* N.endScope()
|
||||
)
|
||||
|
@ -1,7 +1,7 @@
|
||||
package aqua.semantics.expr.func
|
||||
|
||||
import aqua.model.Model
|
||||
import aqua.model.func.{ArrowModel, FuncModel}
|
||||
import aqua.raw.Raw
|
||||
import aqua.raw.arrow.{ArrowRaw, FuncRaw}
|
||||
import aqua.parser.expr.FuncExpr
|
||||
import aqua.parser.lexer.Arg
|
||||
import aqua.semantics.Prog
|
||||
@ -16,13 +16,13 @@ class FuncSem[S[_]](val expr: FuncExpr[S]) extends AnyVal {
|
||||
|
||||
def program[Alg[_]: Monad](implicit
|
||||
N: NamesAlgebra[S, Alg]
|
||||
): Prog[Alg, Model] =
|
||||
): Prog[Alg, Raw] =
|
||||
Prog.after {
|
||||
case arrow: ArrowModel =>
|
||||
N.defineArrow(expr.name, arrow.`type`, isRoot = true) as FuncModel(expr.name.value, arrow)
|
||||
case arrow: ArrowRaw =>
|
||||
N.defineArrow(expr.name, arrow.`type`, isRoot = true) as FuncRaw(expr.name.value, arrow)
|
||||
|
||||
case m =>
|
||||
Model.error("Func must continue with an arrow definition").pure[Alg]
|
||||
Raw.error("Func must continue with an arrow definition").pure[Alg]
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
package aqua.semantics.expr.func
|
||||
|
||||
import aqua.model.func.raw.{FuncOp, MatchMismatchTag, XorTag}
|
||||
import aqua.model.{Model, ValueModel}
|
||||
import aqua.raw.ops.{FuncOp, MatchMismatchTag, XorTag}
|
||||
import aqua.parser.expr.func.IfExpr
|
||||
import aqua.raw.value.ValueRaw
|
||||
import aqua.raw.Raw
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.ValuesAlgebra
|
||||
import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
||||
@ -19,7 +20,7 @@ class IfSem[S[_]](val expr: IfExpr[S]) extends AnyVal {
|
||||
V: ValuesAlgebra[S, Alg],
|
||||
T: TypesAlgebra[S, Alg],
|
||||
A: AbilitiesAlgebra[S, Alg]
|
||||
): Prog[Alg, Model] =
|
||||
): Prog[Alg, Raw] =
|
||||
Prog
|
||||
.around(
|
||||
V.valueToModel(expr.left).flatMap {
|
||||
@ -32,29 +33,28 @@ class IfSem[S[_]](val expr: IfExpr[S]) extends AnyVal {
|
||||
None.pure[Alg]
|
||||
}
|
||||
case None =>
|
||||
V.resolveType(expr.right).as[Option[(ValueModel, ValueModel)]](None)
|
||||
V.resolveType(expr.right).as[Option[(ValueRaw, ValueRaw)]](None)
|
||||
},
|
||||
(r: Option[(ValueModel, ValueModel)], ops: Model) =>
|
||||
r.fold(Model.error("If expression errored in matching types").pure[Alg]) {
|
||||
case (lt, rt) =>
|
||||
ops match {
|
||||
case op: FuncOp =>
|
||||
FuncOp
|
||||
.wrap(
|
||||
XorTag.LeftBiased,
|
||||
FuncOp.wrap(
|
||||
MatchMismatchTag(
|
||||
lt,
|
||||
rt,
|
||||
expr.eqOp.value
|
||||
),
|
||||
op
|
||||
)
|
||||
(r: Option[(ValueRaw, ValueRaw)], ops: Raw) =>
|
||||
r.fold(Raw.error("If expression errored in matching types").pure[Alg]) { case (lt, rt) =>
|
||||
ops match {
|
||||
case op: FuncOp =>
|
||||
FuncOp
|
||||
.wrap(
|
||||
XorTag.LeftBiased,
|
||||
FuncOp.wrap(
|
||||
MatchMismatchTag(
|
||||
lt,
|
||||
rt,
|
||||
expr.eqOp.value
|
||||
),
|
||||
op
|
||||
)
|
||||
.pure[Alg]
|
||||
)
|
||||
.pure[Alg]
|
||||
|
||||
case _ => Model.error("Wrong body of the if expression").pure[Alg]
|
||||
}
|
||||
case _ => Raw.error("Wrong body of the if expression").pure[Alg]
|
||||
}
|
||||
}
|
||||
)
|
||||
.abilitiesScope[S](expr.token)
|
||||
|
@ -1,8 +1,9 @@
|
||||
package aqua.semantics.expr.func
|
||||
|
||||
import aqua.model.func.raw.{FuncOp, OnTag}
|
||||
import aqua.model.{Model, ValueModel}
|
||||
import aqua.raw.ops.{FuncOp, OnTag}
|
||||
import aqua.parser.expr.func.OnExpr
|
||||
import aqua.raw.Raw
|
||||
import aqua.raw.value.ValueRaw
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.ValuesAlgebra
|
||||
import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
||||
@ -22,7 +23,7 @@ class OnSem[S[_]](val expr: OnExpr[S]) extends AnyVal {
|
||||
V: ValuesAlgebra[S, Alg],
|
||||
T: TypesAlgebra[S, Alg],
|
||||
A: AbilitiesAlgebra[S, Alg]
|
||||
): Prog[Alg, Model] =
|
||||
): Prog[Alg, Raw] =
|
||||
Prog.around(
|
||||
(
|
||||
V.ensureIsString(expr.peerId),
|
||||
@ -44,7 +45,7 @@ class OnSem[S[_]](val expr: OnExpr[S]) extends AnyVal {
|
||||
viaVM
|
||||
}
|
||||
<* A.beginScope(expr.peerId),
|
||||
(viaVM: List[ValueModel], ops: Model) =>
|
||||
(viaVM: List[ValueRaw], ops: Raw) =>
|
||||
A.endScope() >> (ops match {
|
||||
case op: FuncOp =>
|
||||
V.valueToModel(expr.peerId).map {
|
||||
@ -57,10 +58,10 @@ class OnSem[S[_]](val expr: OnExpr[S]) extends AnyVal {
|
||||
op
|
||||
)
|
||||
case _ =>
|
||||
Model.error("OnSem: Impossible error")
|
||||
Raw.error("OnSem: Impossible error")
|
||||
}
|
||||
|
||||
case m => Model.error("On body is not an op, it's " + m).pure[Alg]
|
||||
case m => Raw.error("On body is not an op, it's " + m).pure[Alg]
|
||||
})
|
||||
)
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
package aqua.semantics.expr.func
|
||||
|
||||
import aqua.model.Model
|
||||
import aqua.model.func.raw.{FuncOp, ParTag}
|
||||
import aqua.raw.ops.{FuncOp, ParTag}
|
||||
import aqua.parser.expr.func.ParExpr
|
||||
import aqua.raw.Raw
|
||||
import aqua.semantics.Prog
|
||||
import cats.Monad
|
||||
import cats.syntax.applicative.*
|
||||
|
||||
class ParSem[S[_]](val expr: ParExpr[S]) extends AnyVal {
|
||||
|
||||
def program[Alg[_]: Monad]: Prog[Alg, Model] =
|
||||
Prog.after[Alg, Model] {
|
||||
def program[Alg[_]: Monad]: Prog[Alg, Raw] =
|
||||
Prog.after[Alg, Raw] {
|
||||
case g: FuncOp =>
|
||||
FuncOp.wrap(ParTag, g).pure[Alg]
|
||||
case g => g.pure[Alg]
|
||||
|
@ -1,11 +1,9 @@
|
||||
package aqua.semantics.expr.func
|
||||
|
||||
import aqua.model.ValueModel.varName
|
||||
import aqua.model.func.Call
|
||||
import aqua.model.func.raw.{FuncOp, FuncOps, PushToStreamTag}
|
||||
import aqua.model.{LiteralModel, Model, VarModel}
|
||||
import aqua.raw.ops.{Call, FuncOp, FuncOps, PushToStreamTag}
|
||||
import aqua.parser.expr.func.PushToStreamExpr
|
||||
import aqua.parser.lexer.Token
|
||||
import aqua.raw.Raw
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.ValuesAlgebra
|
||||
import aqua.semantics.rules.names.NamesAlgebra
|
||||
@ -45,11 +43,11 @@ class PushToStreamSem[S[_]](val expr: PushToStreamExpr[S]) extends AnyVal {
|
||||
N: NamesAlgebra[S, Alg],
|
||||
T: TypesAlgebra[S, Alg],
|
||||
V: ValuesAlgebra[S, Alg]
|
||||
): Prog[Alg, Model] =
|
||||
): Prog[Alg, Raw] =
|
||||
V.valueToModel(expr.value).flatMap {
|
||||
case Some(vm) =>
|
||||
N.read(expr.stream).flatMap {
|
||||
case None => Model.error("Cannot resolve stream type").pure[Alg]
|
||||
case None => Raw.error("Cannot resolve stream type").pure[Alg]
|
||||
case Some(t) =>
|
||||
ensureStreamElementMatches(
|
||||
expr.token,
|
||||
@ -58,13 +56,13 @@ class PushToStreamSem[S[_]](val expr: PushToStreamExpr[S]) extends AnyVal {
|
||||
vm.lastType
|
||||
).map {
|
||||
case false =>
|
||||
Model.error("Stream type and element type does not match")
|
||||
Raw.error("Stream type and element type does not match")
|
||||
case true =>
|
||||
FuncOps.pushToStream(vm, Call.Export(expr.stream.value, t)): Model
|
||||
FuncOps.pushToStream(vm, Call.Export(expr.stream.value, t)): Raw
|
||||
}
|
||||
}
|
||||
|
||||
case _ => Model.error("Cannot resolve value").pure[Alg]
|
||||
case _ => Raw.error("Cannot resolve value").pure[Alg]
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
package aqua.semantics.expr.func
|
||||
|
||||
import aqua.model.func.raw.{FuncOp, ReturnTag}
|
||||
import aqua.model.Model
|
||||
import aqua.raw.ops.{FuncOp, ReturnTag}
|
||||
import aqua.parser.expr.func.ReturnExpr
|
||||
import aqua.raw.Raw
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.ValuesAlgebra
|
||||
import aqua.semantics.rules.types.TypesAlgebra
|
||||
@ -18,18 +18,18 @@ class ReturnSem[S[_]](val expr: ReturnExpr[S]) extends AnyVal {
|
||||
def program[Alg[_]: Monad](implicit
|
||||
V: ValuesAlgebra[S, Alg],
|
||||
T: TypesAlgebra[S, Alg]
|
||||
): Prog[Alg, Model] =
|
||||
): Prog[Alg, Raw] =
|
||||
expr.values
|
||||
.traverse(v => V.valueToModel(v).map(_.map(v -> _)))
|
||||
.map(_.toList.flatten)
|
||||
.map(NonEmptyList.fromList)
|
||||
.flatMap {
|
||||
case Some(vals) =>
|
||||
T.checkArrowReturn(vals).map[Model] {
|
||||
T.checkArrowReturn(vals).map[Raw] {
|
||||
case true => FuncOp.leaf(ReturnTag(vals.map(_._2)))
|
||||
case false => Model.error("Return types validation failed")
|
||||
case false => Raw.error("Return types validation failed")
|
||||
}
|
||||
case None =>
|
||||
Model.error("Return types resolution failed").pure[Alg]
|
||||
Raw.error("Return types resolution failed").pure[Alg]
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
package aqua.semantics.expr.func
|
||||
|
||||
import aqua.model.Model
|
||||
import aqua.model.func.raw.{FuncOp, XorTag}
|
||||
import aqua.raw.ops.{FuncOp, XorTag}
|
||||
import aqua.parser.expr.func.TryExpr
|
||||
import aqua.raw.Raw
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.ValuesAlgebra
|
||||
import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
||||
import aqua.semantics.rules.types.TypesAlgebra
|
||||
import cats.syntax.applicative._
|
||||
import cats.syntax.applicative.*
|
||||
import cats.Monad
|
||||
|
||||
class TrySem[S[_]](val expr: TryExpr[S]) extends AnyVal {
|
||||
@ -16,13 +16,13 @@ class TrySem[S[_]](val expr: TryExpr[S]) extends AnyVal {
|
||||
V: ValuesAlgebra[S, Alg],
|
||||
T: TypesAlgebra[S, Alg],
|
||||
A: AbilitiesAlgebra[S, Alg]
|
||||
): Prog[Alg, Model] =
|
||||
): Prog[Alg, Raw] =
|
||||
Prog
|
||||
.after[Alg, Model] {
|
||||
.after[Alg, Raw] {
|
||||
case o: FuncOp =>
|
||||
FuncOp.wrap(XorTag.LeftBiased, o).pure[Alg]
|
||||
case _ =>
|
||||
Model.error("Wrong body of the try expression").pure[Alg]
|
||||
Raw.error("Wrong body of the try expression").pure[Alg]
|
||||
}
|
||||
.abilitiesScope(expr.token)
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user