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:
Dmitry Kurinskiy 2022-01-13 10:32:59 +03:00 committed by GitHub
parent a051ab0efc
commit 061e896b63
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
113 changed files with 2087 additions and 1446 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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'.")

View File

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

View File

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

View File

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

View File

@ -0,0 +1,5 @@
package aqua.raw
import aqua.raw.value.ValueRaw
case class ConstantRaw(name: String, value: ValueRaw, allowOverrides: Boolean) extends Raw

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

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

View File

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

View File

@ -0,0 +1,5 @@
package aqua.raw
import aqua.types.Type
case class TypeRaw(name: String, `type`: Type) extends Raw

View File

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

View 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

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

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

View 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`)
}
}

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

View File

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

View 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)"
}

View File

@ -1,4 +1,4 @@
package aqua.model.func.raw
package aqua.raw.ops
import cats.Show
import cats.free.Cofree

View 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`)
}

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

View File

@ -1,3 +0,0 @@
package aqua.model
case class ConstantModel(name: String, value: ValueModel, allowOverrides: Boolean) extends Model

View File

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

View File

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

View File

@ -1,5 +0,0 @@
package aqua.model
import aqua.types.Type
case class TypeModel(name: String, `type`: Type) extends Model

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

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

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

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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]) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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