mirror of
https://github.com/fluencelabs/aqua.git
synced 2024-12-04 14:40:17 +00:00
Support new fluenceJS version in run
command. Print results of a function (#358)
This commit is contained in:
parent
27b885f12d
commit
4eb272e953
12
.github/workflows/test_branch.yml
vendored
12
.github/workflows/test_branch.yml
vendored
@ -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
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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")}) {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
|
@ -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] = ???
|
||||
}
|
||||
|
@ -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
|
||||
|
9
cli/src/main/scala/aqua/run/RunConfig.scala
Normal file
9
cli/src/main/scala/aqua/run/RunConfig.scala
Normal 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--"
|
||||
)
|
@ -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)
|
||||
}
|
||||
)
|
@ -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]],
|
||||
|
@ -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))
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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 =>
|
||||
|
@ -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("")(" " + _)}"
|
||||
}
|
||||
|
@ -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 = {
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -17,7 +17,7 @@
|
||||
"run": "node index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fluencelabs/fluence": "0.12.1"
|
||||
"@fluencelabs/fluence": "0.15.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -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]],
|
||||
|
Loading…
Reference in New Issue
Block a user