mirror of
https://github.com/fluencelabs/aqua.git
synced 2024-12-12 17:55:33 +00:00
Updating compiler backends (#243)
* Updating compiler backends: add FuncRes * TypeScriptService * ServiceRes
This commit is contained in:
parent
38fb824b68
commit
6c498b029b
@ -1,17 +1,15 @@
|
||||
package aqua.backend.air
|
||||
|
||||
import aqua.backend.{Backend, Generated}
|
||||
import aqua.model.AquaContext
|
||||
import aqua.model.transform.GenerationConfig
|
||||
import cats.implicits.toShow
|
||||
import aqua.model.res.AquaRes
|
||||
import cats.syntax.show.*
|
||||
|
||||
object AirBackend extends Backend {
|
||||
|
||||
val ext = ".air"
|
||||
|
||||
override def generate(context: AquaContext, genConf: GenerationConfig): Seq[Generated] = {
|
||||
context.funcs.values.toList.map(fc =>
|
||||
Generated("." + fc.funcName + ext, FuncAirGen(fc).generateAir(genConf).show)
|
||||
)
|
||||
override def generate(aqua: AquaRes): Seq[Generated] = {
|
||||
aqua.funcs.toList
|
||||
.map(fr => Generated("." + fr.funcName + ext, FuncAirGen(fr).generate.show))
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,14 @@
|
||||
package aqua.backend.air
|
||||
|
||||
import aqua.model.func.FuncCallable
|
||||
import aqua.model.transform.{GenerationConfig, Transform}
|
||||
import aqua.model.res.FuncRes
|
||||
|
||||
case class FuncAirGen(func: FuncCallable) {
|
||||
case class FuncAirGen(func: FuncRes) {
|
||||
|
||||
/**
|
||||
* Generates AIR from the function body
|
||||
*/
|
||||
def generateAir(conf: GenerationConfig = GenerationConfig()): Air =
|
||||
def generate: Air =
|
||||
AirGen(
|
||||
Transform.forClient(func, conf)
|
||||
func.body
|
||||
).generate
|
||||
}
|
||||
|
@ -1,23 +1,25 @@
|
||||
package aqua.backend.js
|
||||
|
||||
import aqua.backend.{Backend, Generated}
|
||||
import aqua.model.AquaContext
|
||||
import aqua.model.transform.GenerationConfig
|
||||
import aqua.model.res.AquaRes
|
||||
import cats.data.NonEmptyChain
|
||||
|
||||
object JavaScriptBackend extends Backend {
|
||||
|
||||
val ext = ".js"
|
||||
|
||||
override def generate(context: AquaContext, genConf: GenerationConfig): Seq[Generated] = {
|
||||
val funcs = NonEmptyChain.fromSeq(context.funcs.values.toSeq).map(_.map(JavaScriptFunc(_)))
|
||||
override def generate(aqua: AquaRes): Seq[Generated] = {
|
||||
val funcs = NonEmptyChain.fromChain(
|
||||
aqua.funcs
|
||||
.map(JavaScriptFunc(_))
|
||||
)
|
||||
funcs
|
||||
.map(fs =>
|
||||
Seq(
|
||||
Generated(
|
||||
ext,
|
||||
JavaScriptFile.Header + "\n\n" + fs
|
||||
.map(_.generateJavascript(genConf))
|
||||
.map(_.generate)
|
||||
.toChain
|
||||
.toList
|
||||
.mkString("\n\n")
|
||||
|
@ -1,18 +1,6 @@
|
||||
package aqua.backend.js
|
||||
|
||||
import aqua.backend.Version
|
||||
import aqua.model.AquaContext
|
||||
import aqua.model.transform.GenerationConfig
|
||||
import cats.data.Chain
|
||||
|
||||
case class JavaScriptFile(context: AquaContext) {
|
||||
|
||||
def funcs: Chain[JavaScriptFunc] =
|
||||
Chain.fromSeq(context.funcs.values.toSeq).map(JavaScriptFunc(_))
|
||||
|
||||
def generateJS(conf: GenerationConfig = GenerationConfig()): String =
|
||||
JavaScriptFile.Header + "\n\n" + funcs.map(_.generateJavascript(conf)).toList.mkString("\n\n")
|
||||
}
|
||||
|
||||
object JavaScriptFile {
|
||||
|
||||
|
@ -1,25 +1,22 @@
|
||||
package aqua.backend.js
|
||||
|
||||
import aqua.backend.air.FuncAirGen
|
||||
import aqua.model.func.FuncCallable
|
||||
import aqua.model.transform.GenerationConfig
|
||||
import aqua.types._
|
||||
import cats.syntax.show._
|
||||
import aqua.model.res.FuncRes
|
||||
import aqua.types.*
|
||||
import cats.syntax.show.*
|
||||
|
||||
case class JavaScriptFunc(func: FuncCallable) {
|
||||
case class JavaScriptFunc(func: FuncRes) {
|
||||
|
||||
import JavaScriptFunc._
|
||||
import FuncRes._
|
||||
import func._
|
||||
|
||||
def argsJavaScript: String =
|
||||
func.argNames.mkString(", ")
|
||||
argNames.mkString(", ")
|
||||
|
||||
// TODO: use common functions between TypeScript and JavaScript backends
|
||||
private def genReturnCallback(
|
||||
retType: Type,
|
||||
callbackService: String,
|
||||
respFuncName: String
|
||||
): String = {
|
||||
val body = retType match {
|
||||
private def returnCallback: String = returnType.fold("") { retType =>
|
||||
val respBody = retType match {
|
||||
case OptionType(_) =>
|
||||
""" let [opt] = args;
|
||||
| if (Array.isArray(opt)) {
|
||||
@ -44,42 +41,38 @@ case class JavaScriptFunc(func: FuncCallable) {
|
||||
| resolve(res);""".stripMargin
|
||||
|
||||
}
|
||||
s"""h.onEvent('$callbackService', '$respFuncName', (args) => {
|
||||
| $body
|
||||
s"""h.onEvent('$callbackServiceId', '$respFuncName', (args) => {
|
||||
| $respBody
|
||||
|});
|
||||
|""".stripMargin
|
||||
}
|
||||
|
||||
def generateJavascript(conf: GenerationConfig = GenerationConfig()): String = {
|
||||
def generate: String = {
|
||||
|
||||
val tsAir = FuncAirGen(func).generateAir(conf)
|
||||
val jsAir = FuncAirGen(func).generate
|
||||
|
||||
val setCallbacks = func.args.collect {
|
||||
case (argName, OptionType(_)) =>
|
||||
s"""h.on('${conf.getDataService}', '$argName', () => {return $argName === null ? [] : [$argName];});"""
|
||||
case (argName, _: DataType) =>
|
||||
s"""h.on('${conf.getDataService}', '$argName', () => {return $argName;});"""
|
||||
case (argName, at: ArrowType) =>
|
||||
case Arg(argName, OptionType(_)) =>
|
||||
s"""h.on('$dataServiceId', '$argName', () => {return $argName === null ? [] : [$argName];});"""
|
||||
case Arg(argName, _: DataType) =>
|
||||
s"""h.on('$dataServiceId, '$argName', () => {return $argName;});"""
|
||||
case Arg(argName, at: ArrowType) =>
|
||||
val value = s"$argName(${argsCallToJs(
|
||||
at
|
||||
)})"
|
||||
val expr = at.codomain.uncons.fold(s"$value; return {}")(_ => s"return $value")
|
||||
s"""h.on('${conf.callbackService}', '$argName', (args) => {$expr;});"""
|
||||
val expr = arrowToRes(at).fold(s"$value; return {}")(_ => s"return $value")
|
||||
s"""h.on('$callbackServiceId', '$argName', (args) => {$expr;});"""
|
||||
}
|
||||
.mkString("\n")
|
||||
|
||||
val returnCallback = func.arrowType.codomain.uncons
|
||||
.map(_ => genReturnCallback(func.arrowType.codomain, conf.callbackService, conf.respFuncName))
|
||||
.getOrElse("")
|
||||
|
||||
val returnVal =
|
||||
func.ret.headOption.fold("Promise.race([promise, Promise.resolve()])")(_ => "promise")
|
||||
returnType.headOption.fold("Promise.race([promise, Promise.resolve()])")(_ => "promise")
|
||||
|
||||
// TODO: it could be non-unique
|
||||
val configArgName = "config"
|
||||
val clientArgName = genArgName("client")
|
||||
val configArgName = genArgName("config")
|
||||
|
||||
s"""
|
||||
|export async function ${func.funcName}(client${if (func.args.isEmpty) ""
|
||||
|export async function ${func.funcName}(${clientArgName}${if (func.args.isEmpty) ""
|
||||
else ", "}${argsJavaScript}, $configArgName) {
|
||||
| let request;
|
||||
| $configArgName = $configArgName || {};
|
||||
@ -88,18 +81,18 @@ case class JavaScriptFunc(func: FuncCallable) {
|
||||
| .disableInjections()
|
||||
| .withRawScript(
|
||||
| `
|
||||
|${tsAir.show}
|
||||
|${jsAir.show}
|
||||
| `,
|
||||
| )
|
||||
| .configHandler((h) => {
|
||||
| ${conf.relayVarName.fold("") { r =>
|
||||
s"""h.on('${conf.getDataService}', '$r', () => {
|
||||
| ${relayVarName.fold("") { r =>
|
||||
s"""h.on('$dataServiceId', '$r', () => {
|
||||
| return client.relayPeerId;
|
||||
| });""".stripMargin
|
||||
}}
|
||||
| $setCallbacks
|
||||
| $returnCallback
|
||||
| h.onEvent('${conf.errorHandlingService}', '${conf.errorFuncName}', (args) => {
|
||||
| h.onEvent('$errorHandlerId', '$errorFuncName', (args) => {
|
||||
| // assuming error is the single argument
|
||||
| const [err] = args;
|
||||
| reject(err);
|
||||
@ -107,14 +100,14 @@ case class JavaScriptFunc(func: FuncCallable) {
|
||||
| })
|
||||
| .handleScriptError(reject)
|
||||
| .handleTimeout(() => {
|
||||
| reject('Request timed out for ${func.funcName}');
|
||||
| reject('Request timed out for ${funcName}');
|
||||
| })
|
||||
| if(${configArgName}.ttl) {
|
||||
| r.withTTL(${configArgName}.ttl)
|
||||
| }
|
||||
| request = r.build();
|
||||
| });
|
||||
| await client.initiateFlow(request);
|
||||
| await ${clientArgName}.initiateFlow(request);
|
||||
| return ${returnVal};
|
||||
|}
|
||||
""".stripMargin
|
||||
@ -124,10 +117,7 @@ case class JavaScriptFunc(func: FuncCallable) {
|
||||
|
||||
object JavaScriptFunc {
|
||||
|
||||
def argsToTs(at: ArrowType): String =
|
||||
at.domain.toLabelledList().map(_._1).mkString(", ")
|
||||
|
||||
def argsCallToJs(at: ArrowType): String =
|
||||
at.domain.toList.zipWithIndex.map(_._2).map(idx => s"args[$idx]").mkString(", ")
|
||||
FuncRes.arrowArgIndices(at).map(idx => s"args[$idx]").mkString(", ")
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package aqua.backend
|
||||
|
||||
import aqua.model.AquaContext
|
||||
import aqua.model.transform.GenerationConfig
|
||||
import aqua.model.res.AquaRes
|
||||
|
||||
/**
|
||||
* Compiler backend generates output based on the processed model
|
||||
@ -9,11 +8,10 @@ import aqua.model.transform.GenerationConfig
|
||||
trait Backend {
|
||||
|
||||
/**
|
||||
* Generate the result based on the given [[AquaContext]] and [[GenerationConfig]]
|
||||
* Generate the result based on the given [[AquaRes]]
|
||||
*
|
||||
* @param context Source file context, processed, transformed
|
||||
* @param genConf Generation configuration
|
||||
* @param aqua Source file context, processed, transformed
|
||||
* @return Zero or more [[Generated]] objects, based on arguments
|
||||
*/
|
||||
def generate(context: AquaContext, genConf: GenerationConfig): Seq[Generated]
|
||||
def generate(aqua: AquaRes): Seq[Generated]
|
||||
}
|
||||
|
@ -1,29 +1,13 @@
|
||||
package aqua.backend.ts
|
||||
|
||||
import aqua.backend.{Backend, Generated}
|
||||
import aqua.model.AquaContext
|
||||
import aqua.model.transform.GenerationConfig
|
||||
import aqua.model.res.AquaRes
|
||||
import cats.data.NonEmptyChain
|
||||
|
||||
object TypeScriptBackend extends Backend {
|
||||
|
||||
val ext = ".ts"
|
||||
|
||||
override def generate(context: AquaContext, genConf: GenerationConfig): Seq[Generated] = {
|
||||
val funcs = NonEmptyChain.fromSeq(context.funcs.values.toSeq).map(_.map(TypeScriptFunc(_)))
|
||||
funcs
|
||||
.map(fs =>
|
||||
Seq(
|
||||
Generated(
|
||||
ext,
|
||||
TypeScriptFile.Header + "\n\n" + fs
|
||||
.map(_.generateTypescript(genConf))
|
||||
.toChain
|
||||
.toList
|
||||
.mkString("\n\n")
|
||||
)
|
||||
)
|
||||
)
|
||||
.getOrElse(Seq.empty)
|
||||
}
|
||||
override def generate(res: AquaRes): Seq[Generated] =
|
||||
if (res.isEmpty) Nil else Generated(ext, TypeScriptFile(res).generate) :: Nil
|
||||
}
|
||||
|
@ -1,17 +1,22 @@
|
||||
package aqua.backend.ts
|
||||
|
||||
import aqua.backend.Version
|
||||
import aqua.model.AquaContext
|
||||
import aqua.model.transform.GenerationConfig
|
||||
import cats.data.Chain
|
||||
import aqua.model.res.AquaRes
|
||||
|
||||
case class TypeScriptFile(context: AquaContext) {
|
||||
case class TypeScriptFile(res: AquaRes) {
|
||||
|
||||
def funcs: Chain[TypeScriptFunc] =
|
||||
Chain.fromSeq(context.funcs.values.toSeq).map(TypeScriptFunc(_))
|
||||
import TypeScriptFile.Header
|
||||
|
||||
def generate: String =
|
||||
s"""${Header}
|
||||
|
|
||||
|// Services
|
||||
|${res.services.map(TypeScriptService(_)).map(_.generate).toList.mkString("\n\n")}
|
||||
|
|
||||
|// Functions
|
||||
|${res.funcs.map(TypeScriptFunc(_)).map(_.generate).toList.mkString("\n\n")}
|
||||
|""".stripMargin
|
||||
|
||||
def generateTS(conf: GenerationConfig = GenerationConfig()): String =
|
||||
TypeScriptFile.Header + "\n\n" + funcs.map(_.generateTypescript(conf)).toList.mkString("\n\n")
|
||||
}
|
||||
|
||||
object TypeScriptFile {
|
||||
|
@ -1,33 +1,21 @@
|
||||
package aqua.backend.ts
|
||||
|
||||
import aqua.backend.air.FuncAirGen
|
||||
import aqua.model.func.FuncCallable
|
||||
import aqua.model.transform.GenerationConfig
|
||||
import aqua.types._
|
||||
import cats.syntax.show._
|
||||
import aqua.model.res.FuncRes
|
||||
import aqua.types.*
|
||||
import cats.syntax.show.*
|
||||
|
||||
case class TypeScriptFunc(func: FuncCallable) {
|
||||
case class TypeScriptFunc(func: FuncRes) {
|
||||
|
||||
import TypeScriptFunc._
|
||||
import FuncRes._
|
||||
import func._
|
||||
|
||||
def argsTypescript: String =
|
||||
func.arrowType.domain.toLabelledList().map(ad => s"${ad._1}: " + typeToTs(ad._2)).mkString(", ")
|
||||
args.map(ad => s"${ad.name}: " + typeToTs(ad.`type`)).mkString(", ")
|
||||
|
||||
def generateUniqueArgName(args: List[String], basis: String, attempt: Int): String = {
|
||||
val name = if (attempt == 0) {
|
||||
basis
|
||||
} else {
|
||||
basis + attempt
|
||||
}
|
||||
args.find(_ == name).map(_ => generateUniqueArgName(args, basis, attempt + 1)).getOrElse(name)
|
||||
}
|
||||
|
||||
private def genReturnCallback(
|
||||
retType: Type,
|
||||
callbackService: String,
|
||||
respFuncName: String
|
||||
): String = {
|
||||
val body = retType match {
|
||||
private def returnCallback: String = returnType.fold("") { retType =>
|
||||
val respBody = retType match {
|
||||
case OptionType(_) =>
|
||||
""" let [opt] = args;
|
||||
| if (Array.isArray(opt)) {
|
||||
@ -52,52 +40,43 @@ case class TypeScriptFunc(func: FuncCallable) {
|
||||
| resolve(res);""".stripMargin
|
||||
|
||||
}
|
||||
s"""h.onEvent('$callbackService', '$respFuncName', (args) => {
|
||||
| $body
|
||||
s"""h.onEvent('$callbackServiceId', '$respFuncName', (args) => {
|
||||
| $respBody
|
||||
|});
|
||||
|""".stripMargin
|
||||
}
|
||||
|
||||
def generateTypescript(conf: GenerationConfig = GenerationConfig()): String = {
|
||||
def generate: String = {
|
||||
|
||||
val tsAir = FuncAirGen(func).generateAir(conf)
|
||||
val tsAir = FuncAirGen(func).generate
|
||||
|
||||
// TODO: support multi return
|
||||
val retType =
|
||||
if (func.arrowType.codomain.length > 1) Some(func.arrowType.codomain)
|
||||
else func.arrowType.codomain.uncons.map(_._1)
|
||||
val retTypeTs = retType
|
||||
val retTypeTs = func.returnType
|
||||
.fold("void")(typeToTs)
|
||||
|
||||
val returnCallback = retType
|
||||
.map(t => genReturnCallback(t, conf.callbackService, conf.respFuncName))
|
||||
.getOrElse("")
|
||||
|
||||
val setCallbacks = func.args.collect { // Product types are not handled
|
||||
case (argName, OptionType(_)) =>
|
||||
s"""h.on('${conf.getDataService}', '$argName', () => {return $argName === null ? [] : [$argName];});"""
|
||||
case (argName, _: DataType) =>
|
||||
s"""h.on('${conf.getDataService}', '$argName', () => {return $argName;});"""
|
||||
case (argName, at: ArrowType) =>
|
||||
case Arg(argName, OptionType(_)) =>
|
||||
s"""h.on('$dataServiceId', '$argName', () => {return $argName === null ? [] : [$argName];});"""
|
||||
case Arg(argName, _: DataType) =>
|
||||
s"""h.on('$dataServiceId', '$argName', () => {return $argName;});"""
|
||||
case Arg(argName, at: ArrowType) =>
|
||||
val value = s"$argName(${argsCallToTs(
|
||||
at
|
||||
)})"
|
||||
val expr = at.res.fold(s"$value; return {}")(_ => s"return $value")
|
||||
s"""h.on('${conf.callbackService}', '$argName', (args) => {$expr;});"""
|
||||
val expr = arrowToRes(at).fold(s"$value; return {}")(_ => s"return $value")
|
||||
s"""h.on('$callbackServiceId', '$argName', (args) => {$expr;});"""
|
||||
}
|
||||
.mkString("\n")
|
||||
|
||||
// TODO support multi return
|
||||
val returnVal =
|
||||
func.ret.headOption.fold("Promise.race([promise, Promise.resolve()])")(_ => "promise")
|
||||
returnType.fold("Promise.race([promise, Promise.resolve()])")(_ => "promise")
|
||||
|
||||
val clientArgName = generateUniqueArgName(func.argNames, "client", 0)
|
||||
val configArgName = generateUniqueArgName(func.argNames, "config", 0)
|
||||
val clientArgName = genArgName("client")
|
||||
val configArgName = genArgName("config")
|
||||
|
||||
val configType = "{ttl?: number}"
|
||||
|
||||
s"""
|
||||
|export async function ${func.funcName}($clientArgName: FluenceClient${if (func.args.isEmpty)
|
||||
|export async function ${funcName}($clientArgName: FluenceClient${if (args.isEmpty)
|
||||
""
|
||||
else ", "}${argsTypescript}, $configArgName?: $configType): Promise<$retTypeTs> {
|
||||
| let request: RequestFlow;
|
||||
@ -110,14 +89,14 @@ case class TypeScriptFunc(func: FuncCallable) {
|
||||
| `,
|
||||
| )
|
||||
| .configHandler((h) => {
|
||||
| ${conf.relayVarName.fold("") { r =>
|
||||
s"""h.on('${conf.getDataService}', '$r', () => {
|
||||
| ${relayVarName.fold("") { r =>
|
||||
s"""h.on('$dataServiceId', '$r', () => {
|
||||
| return $clientArgName.relayPeerId!;
|
||||
| });""".stripMargin
|
||||
}}
|
||||
| $setCallbacks
|
||||
| $returnCallback
|
||||
| h.onEvent('${conf.errorHandlingService}', '${conf.errorFuncName}', (args) => {
|
||||
| h.onEvent('$errorHandlerId', '$errorFuncName', (args) => {
|
||||
| // assuming error is the single argument
|
||||
| const [err] = args;
|
||||
| reject(err);
|
||||
@ -125,7 +104,7 @@ case class TypeScriptFunc(func: FuncCallable) {
|
||||
| })
|
||||
| .handleScriptError(reject)
|
||||
| .handleTimeout(() => {
|
||||
| reject('Request timed out for ${func.funcName}');
|
||||
| reject('Request timed out for ${funcName}');
|
||||
| })
|
||||
| if(${configArgName} && ${configArgName}.ttl) {
|
||||
| r.withTTL(${configArgName}.ttl)
|
||||
@ -158,20 +137,18 @@ object TypeScriptFunc {
|
||||
case lt: LiteralType if lt.oneOf(ScalarType.string) => "string"
|
||||
case _: DataType => "any"
|
||||
case at: ArrowType =>
|
||||
s"(${argsToTs(at)}) => ${at.res
|
||||
s"(${argsToTs(at)}) => ${FuncRes
|
||||
.arrowToRes(at)
|
||||
.fold("void")(typeToTs)}"
|
||||
case _ =>
|
||||
// TODO: handle product types in returns
|
||||
"any"
|
||||
}
|
||||
|
||||
def argsToTs(at: ArrowType): String =
|
||||
at.domain
|
||||
.toLabelledList()
|
||||
.map(nt => nt._1 + ": " + typeToTs(nt._2))
|
||||
FuncRes
|
||||
.arrowArgs(at)
|
||||
.map(nt => nt.name + ": " + typeToTs(nt.`type`))
|
||||
.mkString(", ")
|
||||
|
||||
def argsCallToTs(at: ArrowType): String =
|
||||
at.domain.toList.zipWithIndex.map(_._2).map(idx => s"args[$idx]").mkString(", ")
|
||||
FuncRes.arrowArgIndices(at).map(idx => s"args[$idx]").mkString(", ")
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,20 @@
|
||||
package aqua.backend.ts
|
||||
|
||||
import aqua.model.res.ServiceRes
|
||||
|
||||
case class TypeScriptService(srv: ServiceRes) {
|
||||
|
||||
import TypeScriptFunc.typeToTs
|
||||
|
||||
def generate: String =
|
||||
s"""
|
||||
|//${srv.name}
|
||||
|//defaultId = ${srv.defaultId.getOrElse("undefined")}
|
||||
|
|
||||
|${srv.members.map { case (n, v) =>
|
||||
s"//${n}: ${typeToTs(v)}"
|
||||
}.mkString("\n")}
|
||||
|//END ${srv.name}
|
||||
|
|
||||
|""".stripMargin
|
||||
}
|
@ -3,15 +3,16 @@ package aqua.compiler
|
||||
import aqua.backend.Backend
|
||||
import aqua.linker.Linker
|
||||
import aqua.model.AquaContext
|
||||
import aqua.model.res.AquaRes
|
||||
import aqua.model.transform.GenerationConfig
|
||||
import aqua.parser.lift.LiftParser
|
||||
import aqua.semantics.Semantics
|
||||
import cats.data.Validated.{validNec, Invalid, Valid}
|
||||
import cats.data.{Chain, NonEmptyChain, Validated, ValidatedNec}
|
||||
import cats.syntax.applicative._
|
||||
import cats.syntax.flatMap._
|
||||
import cats.syntax.functor._
|
||||
import cats.syntax.traverse._
|
||||
import cats.syntax.applicative.*
|
||||
import cats.syntax.flatMap.*
|
||||
import cats.syntax.functor.*
|
||||
import cats.syntax.traverse.*
|
||||
import cats.{Comonad, Monad}
|
||||
|
||||
object AquaCompiler {
|
||||
@ -47,7 +48,7 @@ object AquaCompiler {
|
||||
}
|
||||
.map(
|
||||
_.map { ap =>
|
||||
val compiled = backend.generate(ap.context, config)
|
||||
val compiled = backend.generate(AquaRes.fromContext(ap.context, config))
|
||||
AquaCompiled(ap.id, compiled)
|
||||
}
|
||||
)
|
||||
|
18
model/src/main/scala/aqua/model/res/AquaRes.scala
Normal file
18
model/src/main/scala/aqua/model/res/AquaRes.scala
Normal file
@ -0,0 +1,18 @@
|
||||
package aqua.model.res
|
||||
|
||||
import aqua.model.AquaContext
|
||||
import aqua.model.transform.{GenerationConfig, Transform}
|
||||
import cats.data.Chain
|
||||
|
||||
case class AquaRes(funcs: Chain[FuncRes], services: Chain[ServiceRes]) {
|
||||
def isEmpty: Boolean = funcs.isEmpty && services.isEmpty
|
||||
}
|
||||
|
||||
object AquaRes {
|
||||
|
||||
def fromContext(ctx: AquaContext, conf: GenerationConfig): AquaRes =
|
||||
AquaRes(
|
||||
funcs = Chain.fromSeq(ctx.funcs.values.toSeq).map(Transform.fn(_, conf)),
|
||||
services = Chain.fromSeq(ctx.services.values.toSeq).map(ServiceRes.fromModel(_))
|
||||
)
|
||||
}
|
51
model/src/main/scala/aqua/model/res/FuncRes.scala
Normal file
51
model/src/main/scala/aqua/model/res/FuncRes.scala
Normal file
@ -0,0 +1,51 @@
|
||||
package aqua.model.res
|
||||
|
||||
import aqua.model.func.FuncCallable
|
||||
import aqua.model.func.resolved.ResolvedOp
|
||||
import aqua.model.transform.GenerationConfig
|
||||
import aqua.types.{ArrowType, Type}
|
||||
import cats.data.Chain
|
||||
import cats.free.Cofree
|
||||
|
||||
case class FuncRes(
|
||||
source: FuncCallable,
|
||||
conf: GenerationConfig,
|
||||
body: Cofree[Chain, ResolvedOp]
|
||||
) {
|
||||
import FuncRes._
|
||||
|
||||
lazy val funcName = source.funcName
|
||||
|
||||
lazy val args: List[Arg] = arrowArgs(source.arrowType)
|
||||
def argNames: List[String] = source.argNames
|
||||
|
||||
def relayVarName: Option[String] = conf.relayVarName
|
||||
def dataServiceId: String = conf.getDataService
|
||||
def callbackServiceId: String = conf.callbackService
|
||||
def respFuncName: String = conf.respFuncName
|
||||
def errorHandlerId: String = conf.errorHandlingService
|
||||
def errorFuncName: String = conf.errorFuncName
|
||||
|
||||
def genArgName(basis: String): String = {
|
||||
val forbidden = args.map(_._1).toSet
|
||||
def genIter(i: Int): String = {
|
||||
val n = if (i < 0) basis else basis + i
|
||||
if (forbidden(n)) genIter(i + 1) else n
|
||||
}
|
||||
genIter(-1)
|
||||
}
|
||||
|
||||
def returnType: Option[Type] = arrowToRes(source.arrowType)
|
||||
}
|
||||
|
||||
object FuncRes {
|
||||
case class Arg(name: String, `type`: Type)
|
||||
def arrowArgs(at: ArrowType): List[Arg] = at.domain.toLabelledList().map(Arg(_, _))
|
||||
|
||||
def arrowArgIndices(at: ArrowType): List[Int] =
|
||||
LazyList.from(0).take(at.domain.length).toList
|
||||
|
||||
def arrowToRes(at: ArrowType): Option[Type] =
|
||||
if (at.codomain.length > 1) Some(at.codomain)
|
||||
else at.codomain.uncons.map(_._1)
|
||||
}
|
20
model/src/main/scala/aqua/model/res/ServiceRes.scala
Normal file
20
model/src/main/scala/aqua/model/res/ServiceRes.scala
Normal file
@ -0,0 +1,20 @@
|
||||
package aqua.model.res
|
||||
|
||||
import aqua.model.ServiceModel
|
||||
import aqua.types.{ArrowType, ScalarType}
|
||||
import aqua.model.LiteralModel
|
||||
|
||||
case class ServiceRes(name: String, members: List[(String, ArrowType)], defaultId: Option[String])
|
||||
|
||||
object ServiceRes {
|
||||
|
||||
def fromModel(sm: ServiceModel): ServiceRes =
|
||||
ServiceRes(
|
||||
name = sm.name,
|
||||
members = sm.arrows.toNel.toList,
|
||||
defaultId = sm.defaultId.collect {
|
||||
case LiteralModel(value, t) if ScalarType.string.acceptsValueOf(t) =>
|
||||
value
|
||||
}
|
||||
)
|
||||
}
|
@ -3,6 +3,7 @@ package aqua.model.transform
|
||||
import aqua.model.func.FuncCallable
|
||||
import aqua.model.VarModel
|
||||
import aqua.model.func.resolved.{NoAir, ResolvedOp}
|
||||
import aqua.model.res.FuncRes
|
||||
import aqua.model.topology.Topology
|
||||
import aqua.types.ScalarType
|
||||
import cats.data.Chain
|
||||
@ -22,7 +23,7 @@ object Transform extends Logging {
|
||||
): Cofree[Chain, ResolvedOp] =
|
||||
tree.copy(tail = tree.tail.map(_.filter(t => filter(t.head)).map(clear(_, filter))))
|
||||
|
||||
def forClient(func: FuncCallable, conf: GenerationConfig): Cofree[Chain, ResolvedOp] = {
|
||||
def fn(func: FuncCallable, conf: GenerationConfig): FuncRes = {
|
||||
val initCallable: InitPeerCallable = InitViaRelayCallable(
|
||||
Chain.fromOption(conf.relayVarName).map(VarModel(_, ScalarType.string))
|
||||
)
|
||||
@ -48,6 +49,10 @@ object Transform extends Logging {
|
||||
callback,
|
||||
conf.respFuncName
|
||||
)
|
||||
|
||||
FuncRes(
|
||||
func,
|
||||
conf,
|
||||
clear(
|
||||
Topology.resolve(
|
||||
errorsCatcher
|
||||
@ -57,5 +62,6 @@ object Transform extends Logging {
|
||||
.tree
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -30,9 +30,9 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
||||
|
||||
val bc = GenerationConfig()
|
||||
|
||||
val fc = Transform.forClient(func, bc)
|
||||
val fc = Transform.fn(func, bc)
|
||||
|
||||
val procFC: Node.Res = fc
|
||||
val procFC: Node.Res = fc.body
|
||||
|
||||
val expectedFC: Node.Res =
|
||||
MakeRes.xor(
|
||||
@ -80,9 +80,9 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
||||
|
||||
val bc = GenerationConfig(wrapWithXor = false)
|
||||
|
||||
val fc = Transform.forClient(func, bc)
|
||||
val fc = Transform.fn(func, bc)
|
||||
|
||||
val procFC: Res = fc
|
||||
val procFC: Res = fc.body
|
||||
|
||||
val expectedFC: Res =
|
||||
MakeRes.seq(
|
||||
@ -141,7 +141,7 @@ class TransformSpec extends AnyFlatSpec with Matchers {
|
||||
|
||||
val bc = GenerationConfig(wrapWithXor = false)
|
||||
|
||||
val res = Transform.forClient(f2, bc): Node.Res
|
||||
val res = Transform.fn(f2, bc).body: Node.Res
|
||||
|
||||
res.equalsOrPrintDiff(
|
||||
MakeRes.seq(
|
||||
|
Loading…
Reference in New Issue
Block a user