Support new fluenceJS version in run command. Print results of a function (#358)

This commit is contained in:
Dima 2021-11-17 10:56:42 +03:00 committed by GitHub
parent 27b885f12d
commit 4eb272e953
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 349 additions and 175 deletions

View File

@ -49,15 +49,15 @@ jobs:
### Update & build
- name: Integration test
run: |
git clone https://github.com/fluencelabs/aqua-playground.git
cd aqua-playground
npm i
cd ..
git clone https://github.com/fluencelabs/aqua-playground.git
sbt "cliJS/fastOptJS"
rm -rf aqua-playground/src/compiled/examples/*
mv cli/.js/target/scala-3.0.2/cli-fastopt.js npm/aqua.mjs
mv cli/.js/target/scala-3.0.2/cli-fastopt.js npm/aqua.js
cd npm
npm i
node aqua.mjs -i ../aqua-playground/aqua/examples -o ../aqua-playground/src/compiled/examples -m ../aqua-playground/node_modules -c "UNIQUE_CONST = 1" -c "ANOTHER_CONST = \"ab\""
cd ../aqua-playground
npm i --save-dev ../npm
npm i
cd ../aqua-playground
npm run compile-aqua -- -c "UNIQUE_CONST = 1" -c "ANOTHER_CONST = \"ab\""
npm run examples

View File

@ -1,5 +1,6 @@
package aqua.backend
import aqua.model.transform.res.FuncRes
import aqua.types.{ArrowType, OptionType, ProductType, Type}
import io.circe.*
import io.circe.generic.auto.*
@ -134,3 +135,25 @@ case class FunctionDef(
argDefs: List[ArgDefinition],
names: NamesConfig
)
object FunctionDef {
def apply(func: FuncRes): FunctionDef = {
val args = func.args.map(a => ArgDefinition.argToDef(a.name, a.`type`))
val config = func.conf
val names = NamesConfig(
config.relayVarName.getOrElse("-relay-"),
config.getDataService,
config.callbackService,
config.callbackService,
config.respFuncName,
config.errorHandlingService,
config.errorFuncName
)
FunctionDef(
func.funcName,
TypeDefinition(func.returnType),
args,
names
)
}
}

View File

@ -27,23 +27,7 @@ case class OutputFunc(func: FuncRes, types: Types) {
val codeLeftSpace = " " * 20
val script = tsAir.show.linesIterator.map(codeLeftSpace + _).mkString("\n")
val args = func.args.map(a => ArgDefinition.argToDef(a.name, a.`type`))
val config = func.conf
val names = NamesConfig(
config.relayVarName.getOrElse("-relay-"),
config.getDataService,
config.callbackService,
config.callbackService,
config.respFuncName,
config.errorHandlingService,
config.errorFuncName
)
val funcDef = FunctionDef(
func.funcName,
TypeDefinition(func.returnType),
args,
names
)
val funcDef = FunctionDef(func)
s"""${funcTypes.generate}
|export function ${func.funcName}(${typed("...args", "any")}) {

View File

@ -1,11 +1,13 @@
package aqua
import aqua.backend.{ArgDefinition, FunctionDef, NamesConfig, TypeDefinition}
import aqua.model.transform.TransformConfig
import aqua.model.transform.res.FuncRes
import aqua.types.Type
import scala.concurrent.{ExecutionContext, Future, Promise}
import scala.scalajs.js
import scala.scalajs.js.JSConverters.*
object CallJsFunction {
@ -15,7 +17,7 @@ object CallJsFunction {
serviceId: String,
fnName: String,
handler: (js.Array[js.Any]) => Unit
) = {
): CallServiceHandler = {
peer.internals.callServiceHandler.use((req, resp, next) => {
if (req.serviceId == serviceId && req.fnName == fnName) {
handler(req.args)
@ -29,58 +31,15 @@ object CallJsFunction {
// Call a function with generated air script
def funcCallJs(
peer: FluencePeer,
air: String,
args: List[(String, js.Any)],
returnType: Option[Type],
config: TransformConfig
functionDef: FunctionDef,
args: List[js.Any]
)(implicit ec: ExecutionContext): Future[Any] = {
val resultPromise: Promise[js.Any] = Promise[js.Any]()
val requestBuilder = new RequestFlowBuilder()
val relayPeerId = peer.getStatus().relayPeerId
requestBuilder
.disableInjections()
.withRawScript(air)
.configHandler((handler, r) => {
handler.on(config.getDataService, config.relayVarName.getOrElse("-relay-"), (_, _) => { relayPeerId })
args.foreach { (fnName, arg) =>
handler.on(config.getDataService, fnName, (_, _) => arg)
}
handler.onEvent(
config.callbackService,
config.respFuncName,
(args, _) => {
if (args.length == 1) {
resultPromise.success(args.pop())
} else if (args.length == 0) {
resultPromise.success(())
} else {
resultPromise.success(args)
}
()
}
)
handler.onEvent(
config.errorHandlingService,
config.errorFuncName,
(args, _) => {
resultPromise.failure(new RuntimeException(args.pop().toString))
()
}
)
})
.handleScriptError((err) => {
resultPromise.failure(new RuntimeException("script error: " + err.toString))
})
.handleTimeout(() => {
if (!resultPromise.isCompleted)
resultPromise.failure(new RuntimeException(s"Request timed out"))
})
peer.internals.initiateFlow(requestBuilder.build()).toFuture.flatMap { _ =>
returnType.fold(resultPromise.success(()).future)(_ => resultPromise.future)
}
V2.callFunction(
args.toJSArray,
FunctionDefJs(functionDef),
air
).toFuture
}
}

View File

@ -1,10 +1,14 @@
package aqua
import aqua.backend.{ArgDefinition, FunctionDef, NamesConfig, TypeDefinition}
import scala.concurrent.Promise
import scala.scalajs.js
import scala.scalajs.js.annotation.JSImport
import scala.scalajs.js.JSConverters.*
import scala.scalajs.js.annotation.{JSExportAll, JSImport}
/***
/**
* *
* This is description of types from Fluence JS library.
* See here for details https://github.com/fluencelabs/fluence-js
*/
@ -62,6 +66,52 @@ trait PeerStatus extends js.Object {
def relayPeerId: String
}
@JSExportAll
case class FunctionDefJs(
functionName: String,
returnType: TypeDefinition,
argDefs: js.Array[ArgDefinition],
names: NamesConfigJs
)
object FunctionDefJs {
def apply(fd: FunctionDef): FunctionDefJs = {
FunctionDefJs(fd.functionName, fd.returnType, fd.argDefs.toJSArray, NamesConfigJs(fd.names))
}
}
@JSExportAll
case class NamesConfigJs(
relay: String,
getDataSrv: String,
callbackSrv: String,
responseSrv: String,
responseFnName: String,
errorHandlingSrv: String,
errorFnName: String
)
object NamesConfigJs {
def apply(nc: NamesConfig): NamesConfigJs = {
NamesConfigJs(
nc.relay,
nc.getDataSrv,
nc.callbackSrv,
nc.responseSrv,
nc.responseFnName,
nc.errorHandlingSrv,
nc.errorFnName
)
}
}
@JSExportAll
case class PeerConfig(
connectTo: String
)
/**
* This class implements the Fluence protocol for javascript-based environments.
* It provides all the necessary features to communicate with Fluence network
@ -74,13 +124,24 @@ class FluencePeer extends js.Object {
def stop(): js.Promise[Unit] = js.native
}
object V2 {
@js.native
@JSImport("@fluencelabs/fluence/dist/internal/compilerSupport/v2.js", "callFunction")
def callFunction(
rawFnArgs: js.Array[js.Any],
`def`: FunctionDefJs,
script: String
): js.Promise[js.Any] = js.native
}
/**
* Public interface to Fluence JS SDK
*/
@js.native
@JSImport("@fluencelabs/fluence", "Fluence")
object Fluence extends js.Object {
def start(str: String): js.Promise[js.Any] = js.native
def start(config: PeerConfig): js.Promise[js.Any] = js.native
def stop(): js.Promise[js.Any] = js.native
def getPeer(): FluencePeer = js.native
def getStatus(): PeerStatus = js.native
@ -95,18 +156,20 @@ object Fluence extends js.Object {
class CallServiceHandler extends js.Object {
def on(
serviceId: String,
fnName: String,
handler: js.Function2[js.Array[js.Any], js.Any, js.Any]
): js.Function0[CallServiceHandler] = js.native
serviceId: String,
fnName: String,
handler: js.Function2[js.Array[js.Any], js.Any, js.Any]
): js.Function0[CallServiceHandler] = js.native
def onEvent(
serviceId: String,
fnName: String,
handler: js.Function2[js.Array[js.Any], js.Any, js.Any]
): js.Function0[CallServiceHandler] = js.native
serviceId: String,
fnName: String,
handler: js.Function2[js.Array[js.Any], js.Any, js.Any]
): js.Function0[CallServiceHandler] = js.native
def use(f: js.Function3[CallServiceData, CallServiceResult, js.Function0[Unit], Unit]): CallServiceHandler = js.native
def use(
f: js.Function3[CallServiceData, CallServiceResult, js.Function0[Unit], Unit]
): CallServiceHandler = js.native
}
/**
@ -127,6 +190,7 @@ class RequestFlow extends js.Object {}
@JSImport("@fluencelabs/fluence/dist/internal/compilerSupport/v1.js", "RequestFlowBuilder")
class RequestFlowBuilder extends js.Object {
def withRawScript(air: String): RequestFlowBuilder = js.native
def configHandler(f: js.Function2[CallServiceHandler, js.Any, Unit]): RequestFlowBuilder =
js.native
def disableInjections(): RequestFlowBuilder = js.native

View File

@ -1,17 +1,22 @@
package aqua
import aqua.backend.Generated
import aqua.backend.air.AirBackend
import aqua.backend.air.{AirBackend, FuncAirGen}
import aqua.backend.js.JavaScriptBackend
import aqua.backend.ts.TypeScriptBackend
import aqua.backend.{FunctionDef, Generated}
import aqua.compiler.{AquaCompiled, AquaCompiler}
import aqua.files.{AquaFileSources, AquaFilesIO, FileModuleId}
import aqua.io.AquaFileError
import aqua.model.transform.TransformConfig
import aqua.model.transform.res.FuncRes
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, VarModel}
import aqua.parser.expr.func.CallArrowExpr
import aqua.parser.lexer.Literal
import aqua.parser.lift.FileSpan
import aqua.run.RunConfig
import aqua.types.{ArrowType, NilType, ScalarType}
import cats.data.*
import cats.effect.kernel.{Async, Clock}
import cats.effect.syntax.async.*
@ -22,12 +27,13 @@ import cats.syntax.flatMap.*
import cats.syntax.functor.*
import cats.syntax.monad.*
import cats.syntax.show.*
import cats.{~>, Id, Monad}
import cats.{Id, Monad, ~>}
import fs2.io.file.{Files, Path}
import scribe.Logging
import scala.concurrent.{ExecutionContext, Future, Promise}
import scala.scalajs.js
import scala.scalajs.js.JSON
import scala.scalajs.js.annotation.*
object RunCommand extends Logging {
@ -38,34 +44,84 @@ object RunCommand extends Logging {
* @param air code to call
* @return
*/
def funcCall(multiaddr: String, air: Generated, config: TransformConfig)(implicit
def funcCall(multiaddr: String, air: String, functionDef: FunctionDef, config: RunConfig)(implicit
ec: ExecutionContext
): Future[Validated[String, Unit]] = {
): Future[Unit] = {
(for {
_ <- Fluence
.start(multiaddr)
.start(PeerConfig(connectTo = multiaddr))
.toFuture
peer = Fluence.getPeer()
promise = Promise.apply[Unit]()
_ = CallJsFunction.registerUnitService(
peer,
"console",
"print",
args => println("print: " + args)
config.consoleServiceId,
config.printFunctionName,
args => {
// if an input function returns a result, our success will be after it is printed
// otherwise finish after JS SDK will finish sending a request
println(args)
promise.success(())
}
)
result <- CallJsFunction.funcCallJs(
peer,
air.content,
Nil,
None, // TODO
config
callFuture = CallJsFunction.funcCallJs(
air,
functionDef,
List.empty
)
_ <- Future.firstCompletedOf(promise.future :: callFuture :: Nil)
_ <- peer.stop().toFuture
} yield {
Validated.Valid(())
})
} yield {})
}
val generatedFuncName = "callerUniqueFunction"
private def findFunction(contexts: Chain[AquaContext], funcName: String): Option[FuncCallable] =
contexts
.flatMap(_.exports.map(e => Chain.fromSeq(e.funcs.values.toList)).getOrElse(Chain.empty))
.find(_.funcName == funcName)
// Wrap a function that it will be called in another function, and pass results to a `print` service, i.e.:
// func wrapFunc():
// res <- funcCallable(args:_*)
// Console.print(res)
// TODO: now it supports only one result. If funcCallable will return multiple results, only first will be printed
private def wrapCall(
funcName: String,
funcCallable: FuncCallable,
args: List[LiteralModel],
config: RunConfig
): FuncCallable = {
// pass results to a printing service if an input function returns a result
// otherwise just call it
val body = funcCallable.arrowType.codomain.toList match {
case Nil =>
FuncOp.leaf(CallArrowTag(funcName, Call(args, Nil)))
case types =>
val (variables, exports) = types.zipWithIndex.map { case (t, idx) =>
val name = config.resultName + idx
(VarModel(name, t), Call.Export(name, t))
}.unzip
val callFuncTag =
CallArrowTag(funcName, Call(args, exports))
val callServiceTag = CallServiceTag(
LiteralModel.quote(config.consoleServiceId),
config.printFunctionName,
Call(variables, Nil)
)
FuncOps.seq(FuncOp.leaf(callFuncTag), FuncOp.leaf(callServiceTag))
}
FuncCallable(
config.functionWrapperName,
body,
// no arguments and returns nothing
ArrowType(NilType, NilType),
Nil,
Map(funcName -> funcCallable),
Map.empty
)
}
/**
* Runs a function that is located in `input` file with FluenceJS SDK. Returns no output
@ -77,51 +133,45 @@ object RunCommand extends Logging {
def run[F[_]: Files: AquaIO: Async](
multiaddr: String,
func: String,
args: List[LiteralModel],
input: Path,
imports: List[Path],
config: TransformConfig = TransformConfig()
transformConfig: TransformConfig = TransformConfig(),
runConfig: RunConfig = RunConfig()
)(implicit ec: ExecutionContext): F[Unit] = {
implicit val aio: AquaIO[IO] = new AquaFilesIO[IO]
val generatedFile = Path("./.aqua/call0.aqua").absolute
val absInput = input.absolute
val code =
s"""import "${absInput.toString}"
|
|func $generatedFuncName():
| $func
|""".stripMargin
val sources = new AquaFileSources[F](input, imports)
for {
_ <- AquaIO[F].writeFile(generatedFile, code).value
importsWithInput = absInput +: imports.map(_.absolute)
sources = new AquaFileSources[F](generatedFile, importsWithInput)
// compile only context to wrap and call function later
compileResult <- Clock[F].timed(
AquaCompiler
.compile[F, AquaFileError, FileModuleId, FileSpan.F](
.compileToContext[F, AquaFileError, FileModuleId, FileSpan.F](
sources,
SpanParser.parser,
AirBackend,
config
transformConfig
)
)
(compileTime, airV) = compileResult
(compileTime, contextV) = compileResult
callResult <- Clock[F].timed {
airV match {
case Validated.Valid(airC: Chain[AquaCompiled[FileModuleId]]) =>
// Cause we generate input with only one function, we should have only one air compiled content
airC.headOption
.flatMap(_.compiled.headOption)
.map { air =>
Async[F].fromFuture {
funcCall(multiaddr, air, config).map(_.toValidatedNec).pure[F]
}
}
.getOrElse {
Validated
.invalidNec("Unexpected. There could be only one generated function.")
.pure[F]
contextV match {
case Validated.Valid(contextC: Chain[AquaContext]) =>
findFunction(contextC, func).map { funcCallable =>
// call an input function from a generated function
val wrapped = wrapCall(func, funcCallable, args, runConfig)
val funcRes = Transform.fn(wrapped, transformConfig)
val definitions = FunctionDef(funcRes)
val air = FuncAirGen(funcRes).generate.show
Async[F].fromFuture {
funcCall(multiaddr, air, definitions, runConfig).pure[F]
}.map { _ =>
Validated.validNec(())
}
}.getOrElse(Validated.invalidNec(s"There is no function called '$func'").pure[F])
case Validated.Invalid(errs) =>
import ErrorRendering.showError
Validated.invalid(errs.map(_.show)).pure[F]

View File

@ -1,5 +1,8 @@
package aqua
import aqua.model.LiteralModel
import aqua.model.transform.TransformConfig
import aqua.run.RunConfig
import cats.Monad
import cats.effect.IO
import cats.effect.kernel.Async
@ -9,10 +12,13 @@ import scala.concurrent.{ExecutionContext, Future}
object RunCommand {
def run[F[_]: Monad: Files: AquaIO: Async](
def run[F[_]: Files: AquaIO: Async](
multiaddr: String,
func: String,
args: List[LiteralModel],
input: Path,
imps: List[Path]
imports: List[Path],
transformConfig: TransformConfig = TransformConfig(),
runConfig: RunConfig = RunConfig()
)(implicit ec: ExecutionContext): F[Unit] = ???
}

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.run.RunOpts
import cats.data.Validated.{Invalid, Valid}
import cats.data.{NonEmptyList, Validated, ValidatedNec, ValidatedNel}
import cats.effect.kernel.Async

View File

@ -0,0 +1,9 @@
package aqua.run
// `run` command configuration
case class RunConfig(
consoleServiceId: String = "--after-callback-srv-service--",
printFunctionName: String = "print-and-stop",
resultName: String = "res",
functionWrapperName: String = "--someFuncToRun--"
)

View File

@ -1,8 +1,11 @@
package aqua
package aqua.run
import aqua.RunCommand
import aqua.model.LiteralModel
import aqua.parser.expr.func.CallArrowExpr
import aqua.parser.lexer.{Literal, VarLambda}
import aqua.parser.lift.LiftParser.Implicits.idLiftParser
import aqua.parser.lift.Span
import aqua.{AppOpts, AquaIO, RunCommand}
import cats.data.{NonEmptyList, Validated}
import cats.effect.kernel.Async
import cats.effect.{ExitCode, IO}
@ -10,11 +13,11 @@ import cats.syntax.applicative.*
import cats.syntax.apply.*
import cats.syntax.flatMap.*
import cats.syntax.functor.*
import cats.{~>, Id, Monad}
import cats.{Id, Monad, ~>}
import com.monovore.decline.{Command, Opts}
import fs2.io.file.Files
import scala.concurrent.{ExecutionContext, Future}
import scala.concurrent.ExecutionContext
object RunOpts {
@ -25,15 +28,42 @@ object RunOpts {
"/dns4/kras-00.fluence.dev/tcp/19001/wss/p2p/12D3KooWR4cv1a8tv7pps4HH6wePNaK6gf1Hww5wcCMzeWxyNw51"
)
val funcNameOpt: Opts[String] =
def spanToId: Span.S ~> Id = new (Span.S ~> Id) {
override def apply[A](span: Span.S[A]): Id[A] = {
span._2
}
}
val funcOpt: Opts[(String, List[LiteralModel])] =
Opts
.option[String]("func", "Function to call with args", "f")
.mapValidated { str =>
CallArrowExpr.funcOnly.parseAll(str) match {
case Right(f) =>
val expr = f.mapK(spanToId)
val hasVars = expr.args.exists {
case VarLambda(_, _) => true
case _ => false
}
if (hasVars) {
Validated.invalidNel("Function can have only literal arguments, no variables or constants allowed at the moment")
} else {
val args = expr.args.collect { case l @ Literal(_, _) =>
LiteralModel(l.value, l.ts)
}
Validated.validNel((expr.funcName.value, args))
}
case Left(err) => Validated.invalid(err.expected.map(_.context.mkString("\n")))
}
}
def runOptions[F[_]: Files: AquaIO: Async](implicit
ec: ExecutionContext
): Opts[F[cats.effect.ExitCode]] =
(AppOpts.inputOpts[F], AppOpts.importOpts[F], multiaddrOpt, funcNameOpt).mapN {
(inputF, importF, multiaddr, func) =>
(AppOpts.inputOpts[F], AppOpts.importOpts[F], multiaddrOpt, funcOpt).mapN {
case (inputF, importF, multiaddr, (func, args)) =>
for {
inputV <- inputF
impsV <- importF
@ -44,7 +74,7 @@ object RunOpts {
_ => cats.effect.ExitCode.Error.pure[F],
{ imps =>
RunCommand
.run(multiaddr, func, input, imps)
.run(multiaddr, func, args, input, imps)
.map(_ => cats.effect.ExitCode.Success)
}
)

View File

@ -9,8 +9,8 @@ import aqua.parser.lift.{LiftParser, Span}
import aqua.parser.{Ast, ParserError}
import aqua.semantics.Semantics
import aqua.semantics.header.HeaderSem
import cats.data.Validated.{Invalid, Valid, validNec}
import cats.data.*
import cats.data.Validated.{Invalid, Valid, validNec}
import cats.parse.Parser0
import cats.syntax.applicative.*
import cats.syntax.flatMap.*
@ -22,12 +22,11 @@ import scribe.Logging
object AquaCompiler extends Logging {
def compile[F[_]: Monad, E, I: Order, S[_]: Comonad](
private def compileRaw[F[_]: Monad, E, I: Order, S[_]: Comonad](
sources: AquaSources[F, E, I],
parser: I => String => ValidatedNec[ParserError[S], Ast[S]],
backend: Backend,
config: TransformConfig
): F[ValidatedNec[AquaError[I, E, S], Chain[AquaCompiled[I]]]] = {
): F[ValidatedNec[AquaError[I, E, S], Chain[AquaProcessed[I]]]] = {
import config.aquaContextMonoid
type Err = AquaError[I, E, S]
type Ctx = NonEmptyMap[I, AquaContext]
@ -64,7 +63,7 @@ object AquaCompiler extends Logging {
)
)
.map(
_.andThen(modules =>
_.andThen { modules =>
logger.trace("linking modules...")
Linker
.link[I, AquaError[I, E, S], ValidatedCtx](
@ -86,19 +85,42 @@ object AquaCompiler extends Logging {
case (acc, (_, Invalid(errs))) =>
acc combine Invalid(errs)
}
.map(
_.map { ap =>
logger.trace("generating output...")
val res = AquaRes.fromContext(ap.context, config)
val compiled = backend.generate(res)
AquaCompiled(ap.id, compiled, res.funcs.length.toInt, res.services.length.toInt)
}
)
}
)
}
)
}
// Get only compiled model
def compileToContext[F[_]: Monad, E, I: Order, S[_]: Comonad](
sources: AquaSources[F, E, I],
parser: I => String => ValidatedNec[ParserError[S], Ast[S]],
config: TransformConfig
): F[ValidatedNec[AquaError[I, E, S], Chain[AquaContext]]] = {
compileRaw(sources, parser, config).map(_.map {
_.map { ap =>
logger.trace("generating output...")
ap.context
}
})
}
// Get result generated by backend
def compile[F[_]: Monad, E, I: Order, S[_]: Comonad](
sources: AquaSources[F, E, I],
parser: I => String => ValidatedNec[ParserError[S], Ast[S]],
backend: Backend,
config: TransformConfig
): F[ValidatedNec[AquaError[I, E, S], Chain[AquaCompiled[I]]]] = {
compileRaw(sources, parser, config).map(_.map {
_.map { ap =>
logger.trace("generating output...")
val res = AquaRes.fromContext(ap.context, config)
val compiled = backend.generate(res)
AquaCompiled(ap.id, compiled, res.funcs.length.toInt, res.services.length.toInt)
}
})
}
def compileTo[F[_]: Monad, E, I: Order, S[_]: Comonad, T](
sources: AquaSources[F, E, I],
parser: I => String => ValidatedNec[ParserError[S], Ast[S]],

View File

@ -3,6 +3,7 @@ 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 =
@ -11,6 +12,7 @@ case class Call(args: List[ValueModel], exportTo: List[Call.Export]) {
exportTo
)
// TODO docs
def mapExport(f: String => String): Call = copy(exportTo = exportTo.map(_.mapName(f)))
def argVarNames: Set[String] = args.collect { case VarModel(name, _, _) =>
@ -22,7 +24,7 @@ case class Call(args: List[ValueModel], exportTo: List[Call.Export]) {
}
object Call {
// TODO docs
case class Export(name: String, `type`: Type) {
def mapName(f: String => String): Export = copy(f(name))

View File

@ -9,6 +9,7 @@ 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,

View File

@ -2,27 +2,31 @@ package aqua.model.transform
import aqua.model.VarModel
import aqua.model.func.FuncCallable
import aqua.model.transform.funcop.*
import aqua.model.transform.res.{FuncRes, NoAir, ResolvedOp}
import aqua.model.transform.topology.Topology
import aqua.model.transform.funcop.*
import aqua.types.ScalarType
import cats.data.Chain
import cats.free.Cofree
import scribe.Logging
// TODO: doc
object Transform extends Logging {
// TODO: doc
def defaultFilter(t: ResolvedOp): Boolean = t match {
case _: NoAir => false
case _ => true
}
// TODO: doc
def clear(
tree: Cofree[Chain, ResolvedOp],
filter: ResolvedOp => Boolean = defaultFilter
): Cofree[Chain, ResolvedOp] =
tree.copy(tail = tree.tail.map(_.filter(t => filter(t.head)).map(clear(_, filter))))
// TODO: doc/rename
def fn(func: FuncCallable, conf: TransformConfig): FuncRes = {
val initCallable: InitPeerCallable = InitViaRelayCallable(
Chain.fromOption(conf.relayVarName).map(VarModel(_, ScalarType.string))
@ -39,11 +43,13 @@ object Transform extends Logging {
conf.relayVarName.map(_ -> ScalarType.string).toList ::: func.arrowType.domain.labelledData
)
// TODO: comments
val transform =
initCallable.transform _ compose argsProvider.transform
val callback = initCallable.service(conf.callbackSrvId)
// TODO: comments/rename value
val wrapFunc = ResolveFunc(
transform,
callback,
@ -54,10 +60,13 @@ object Transform extends Logging {
func,
conf,
clear(
// TODO: comments
Topology.resolve(
errorsCatcher
.transform(
wrapFunc.resolve(func).value
// TODO: comments
wrapFunc.
resolve(func).value
)
.tree
)

View File

@ -4,6 +4,7 @@ import aqua.model.{AquaContext, LiteralModel, ValueModel, VarModel}
import aqua.types.ScalarType
import cats.kernel.Monoid
// TODO docs
case class TransformConfig(
getDataService: String = "getDataSrv",
callbackService: String = "callbackSrv",
@ -46,7 +47,7 @@ case class TransformConfig(
VarModel.lastError
)
val constantsMap =
val constantsMap: Map[String, ValueModel] =
(hostPeerId :: initPeerId :: nil :: lastError :: constants)
.map(c => c.name -> c.value)
.toMap
@ -66,6 +67,7 @@ case class TransformConfig(
object TransformConfig {
case class Const(name: String, value: ValueModel)
// TODO docs/rename? why it is unused
def forHost: TransformConfig =
TransformConfig(wrapWithXor = false, relayVarName = None)
}

View File

@ -3,9 +3,10 @@ package aqua.model.transform.funcop
import aqua.model.func.*
import aqua.model.func.raw.{FuncOp, FuncOps}
import aqua.model.{ValueModel, VarModel}
import aqua.types.{ArrayType, ArrowType, ConsType, NilType, ProductType, StreamType}
import aqua.types.*
import cats.Eval
// TODO: doc
case class ResolveFunc(
transform: FuncOp => FuncOp,
callback: (String, Call) => FuncOp,
@ -16,6 +17,7 @@ case class ResolveFunc(
private val returnVar: String = "-return-"
// TODO: doc
def returnCallback(retModel: List[ValueModel]): FuncOp =
callback(
respFuncName,
@ -25,6 +27,7 @@ case class ResolveFunc(
)
)
// TODO: doc
def arrowToCallback(name: String, arrowType: ArrowType): FuncCallable = {
val (args, call, ret) = ArgsCall.arrowToArgsCallRet(arrowType)
FuncCallable(
@ -37,6 +40,7 @@ case class ResolveFunc(
)
}
// TODO: doc/rename
def wrap(func: FuncCallable): FuncCallable = {
val returnType = ProductType(func.ret.map(_.lastType).map {
// we mustn't return a stream in response callback to avoid pushing stream to `-return-` value
@ -76,6 +80,7 @@ case class ResolveFunc(
)
}
// TODO: doc/rename
def resolve(
func: FuncCallable,
funcArgName: String = "_func"

View File

@ -1,10 +1,11 @@
package aqua.model.transform.res
import aqua.model.AquaContext
import aqua.model.transform.res.*
import aqua.model.transform.{Transform, TransformConfig}
import aqua.model.transform.res._
import cats.data.Chain
// TODO: doc
case class AquaRes(funcs: Chain[FuncRes], services: Chain[ServiceRes]) {
def isEmpty: Boolean = funcs.isEmpty && services.isEmpty
}
@ -12,6 +13,7 @@ case class AquaRes(funcs: Chain[FuncRes], services: Chain[ServiceRes]) {
object AquaRes {
private val blank = AquaRes(Chain.nil, Chain.nil)
// TODO: doc/rename
def fromContext(ctx: AquaContext, conf: TransformConfig): AquaRes =
ctx.exports
.map(ex =>

View File

@ -3,6 +3,7 @@ package aqua.model.transform.res
import aqua.model.ValueModel
import aqua.model.func.Call
// TODO docs
case class CallRes(args: List[ValueModel], exportTo: Option[Call.Export]) {
override def toString: String = s"[${args.mkString(" ")}]${exportTo.fold("")(" " + _)}"
}

View File

@ -6,6 +6,7 @@ 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,
@ -13,7 +14,7 @@ case class FuncRes(
) {
import FuncRes.*
lazy val funcName = source.funcName
lazy val funcName: String = source.funcName
lazy val args: List[Arg] = arrowArgs(source.arrowType)
def argNames: List[String] = source.argNames
@ -25,6 +26,7 @@ case class FuncRes(
def errorHandlerId: String = conf.errorHandlingService
def errorFuncName: String = conf.errorFuncName
// TODO: docs
def genArgName(basis: String): String = {
val forbidden = args.map(_._1).toSet
def genIter(i: Int): String = {

View File

@ -9,6 +9,7 @@ import cats.Eval
import cats.data.Chain
import cats.free.Cofree
// TODO docs
object MakeRes {
val nilTail: Eval[Chain[Res]] = Eval.now(Chain.empty)

View File

@ -1,8 +1,9 @@
package aqua.model.transform.res
import aqua.model.{ValueModel, VarModel}
import aqua.model.func.Call
import aqua.model.{ValueModel, VarModel}
// TODO docs to all traits and objects
sealed trait ResolvedOp
sealed trait NoAir extends ResolvedOp

View File

@ -3,6 +3,7 @@ package aqua.model.transform.res
import aqua.model.{LiteralModel, ServiceModel}
import aqua.types.{ArrowType, ScalarType}
// TODO: docs
case class ServiceRes(name: String, members: List[(String, ArrowType)], defaultId: Option[String])
object ServiceRes {

View File

@ -17,7 +17,7 @@
"run": "node index.js"
},
"dependencies": {
"@fluencelabs/fluence": "0.12.1"
"@fluencelabs/fluence": "0.15.0"
},
"repository": {
"type": "git",

View File

@ -3,13 +3,12 @@ package aqua.parser.expr.func
import aqua.parser.Expr
import aqua.parser.expr.func.CallArrowExpr
import aqua.parser.lexer.Token.*
import aqua.parser.lexer.{Ability, Name, Value}
import aqua.parser.lift.LiftParser
import aqua.parser.lexer.{Ability, Name, Value, VarLambda}
import aqua.parser.lift.{LiftParser, Span}
import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
import cats.data.NonEmptyList
import cats.parse.{Parser as P, Parser0 as P0}
import cats.{Comonad, ~>}
import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
case class CallArrowExpr[F[_]](
variables: List[Name[F]],