Generate typescript as a whole

This commit is contained in:
dmitry 2021-03-19 16:00:28 +03:00
parent 61781dd5bd
commit 7fef0d3906
6 changed files with 130 additions and 21 deletions

View File

@ -16,9 +16,9 @@ object Aqua {
def validate(input: String): ValidatedNel[AquaError, Model] =
parse(input).andThen(ast => Semantics.validate(ast).leftMap(_.map(ts => CompilerError(ts._1.unit._1, ts._2))))
def generate(input: String): ValidatedNel[AquaError, Queue[String]] =
def generate(input: String): ValidatedNel[AquaError, String] =
validate(input).map {
case g: ScriptModel => g.generateAir
case _ => Queue.empty
case g: ScriptModel => g.generateTypescript
case _ => "//No input given"
}
}

View File

@ -94,7 +94,7 @@ object Air {
private def show(depth: Int, air: Air): String = {
def showNext(a: Air) = show(depth + 1, a)
val space = "\t" * depth
val space = " " * depth
s"$space(${air.keyword.value}" +
(air match {
case Air.Null ""

View File

@ -25,5 +25,6 @@ class SrvCallable(srvId: DataView, fnName: String) extends ArrowCallable {
class SrvCallableOnPeer(peerId: DataView, srvId: DataView, fnName: String) extends ArrowCallable {
override def toCallGen(args: List[DataView], result: Option[String]): AirGen =
// TODO: hop via relay, if needed!
ServiceCallGen(srvId, fnName, args, result).wrap(ctx => (ctx.copy(peerId = peerId), _.copy(peerId = ctx.peerId)))
}

View File

@ -1,35 +1,132 @@
package aqua.model
import aqua.generator.DataView.InitPeerId
import aqua.generator.DataView.{InitPeerId, StringScalar}
import aqua.generator.{Air, AirContext, AirGen, ArrowCallable, DataView, FuncCallable, SrvCallableOnPeer}
import aqua.semantics.{ArrowType, DataType}
import aqua.semantics.{ArrayType, ArrowType, DataType, Type}
import cats.data.{Chain, NonEmptyChain}
import cats.syntax.show._
case class FuncModel(
name: String,
args: List[(String, Either[DataType, ArrowType])],
ret: Option[DataView],
ret: Option[(DataView, Type)],
body: FuncOp
) extends Model {
def bodyGen: AirGen = body.toAirGen
def getDataService: String = "getDataSrv"
def callbackService: String = "callbackSrv"
def callable: ArrowCallable =
new FuncCallable(args.map(_._1), ret, bodyGen)
new FuncCallable(args.map(_._1), ret.map(_._1), bodyGen)
def typeToTs(t: Type): String = t match {
case ArrayType(t) => typeToTs(t) + "[]"
case dt: DataType => "any" // TODO render types
case at: ArrowType =>
s"(${argsToTs(at)}) => ${at.res
.fold("void")(_ => "any")}"
}
def argsToTs(at: ArrowType): String =
at.args.map(typeToTs).zipWithIndex.map(_.swap).map(kv => "arg" + kv._1 + ": " + kv._2).mkString(", ")
def argsCallToTs(at: ArrowType): String =
at.args.zipWithIndex.map(_._2).map("arg" + _).mkString(", ")
def argsTypescript: String =
args.map {
case (n, Left(t)) => s"${n}: " + typeToTs(t)
case (n, Right(at)) => s"${n}: " + typeToTs(at)
}.mkString(", ")
def airContext(acc: Map[String, ArrowCallable]): AirContext =
AirContext(
data = args.collect { //TODO preload these variables
case (an, Left(_)) =>
an -> DataView.Variable(an)
}.toMap,
arrows = acc ++ args.collect { case (an, Right(_)) =>
an -> new SrvCallableOnPeer(InitPeerId, DataView.StringScalar(callbackService), an)
}.toMap,
vars = args.map(_._1).toSet
)
def generateAir(acc: Map[String, ArrowCallable]): Air =
bodyGen
.generate(
AirContext(
data = args.collect { //TODO preload these variables
case (an, Left(_)) =>
an -> DataView.Variable(an)
}.toMap,
arrows = acc ++ args.collect { case (an, Right(_)) =>
an -> new SrvCallableOnPeer(InitPeerId, DataView.StringScalar("callback"), an)
}.toMap,
vars = args.map(_._1).toSet
)
)
.generate(airContext(acc))
._2
def generateTypescript(acc: Map[String, ArrowCallable]): String = {
def getDataOp(name: String): FuncOp =
CoalgebraModel(
Some(ServiceModel(getDataService, StringScalar("\"" + getDataService + "\""))),
name,
Nil,
Some(name)
)
val air = SeqModel(
NonEmptyChain.fromChainAppend(
Chain.fromSeq(
args.collect { case (argName, Left(_)) =>
getDataOp(name)
}
),
body
)
).toAirGen.generate(airContext(acc))._2.show
val setCallbacks = args.map {
case (argName, Left(t)) =>
s"""h.on('$getDataService', '$argName', () => {return $argName;});"""
case (argName, Right(at)) =>
s"""h.on('$callbackService', '$argName', (${argsToTs(at)}) => {return $argName(${argsCallToTs(at)});});"""
}.mkString("\n")
s"""
|export async function ${name}(client: FluenceClient, ${argsTypescript}): Promise<${ret
.map(_._2)
.fold("void")(typeToTs)}> {
| let request;
| const promise = new Promise<string>((resolve, reject) => {
| request = new RequestFlowBuilder()
| .withRawScript(
| `
|$air
| `,
| )
| .configHandler((h) => {
| h.on('getRelayService', 'getRelay', () => {
| return client.relayPeerId;
| });
| h.on('getRelayService', 'hasReleay', () => {
| return client.relayPeerId !== undefined;
| });
| $setCallbacks
| h.on('nameForServiceWhichResolvesPromise', 'callbackOrAnythingReally', (args) => {
| // args is an array of all the arguments to function.
| // Extract the right one from the args. If there is only 1 item, you can always use
| // the costruct below
| const [res] = args;
| resolve(res);
| });
| h.on('nameOfServiceWhereToSendXorError', 'errorProbably', (args) => {
| // assuming error is the single argument
| const [err] = args;
| reject(err);
| });
| })
| .handleTimeout(() => {
| reject('message for timeout');
| })
| .build();
| });
| await client.initiateFlow(request);
| return promise;
|}
""".stripMargin
}
}

View File

@ -19,4 +19,15 @@ case class ScriptModel(funcs: Queue[FuncModel]) extends Model {
}
._2
def generateTypescript: String =
"""import { FluenceClient, PeerIdB58 } from '@fluencelabs/fluence';
|import { RequestFlowBuilder } from '@fluencelabs/fluence/dist/api.unstable';
|
|""".stripMargin ++ funcs
.foldLeft((Map.empty[String, ArrowCallable], Queue.empty[String])) { case ((funcsAcc, outputAcc), func) =>
funcsAcc.updated(func.name, func.callable) -> outputAcc.enqueue(func.generateTypescript(funcsAcc))
}
._2
.mkString("\n\n")
}

View File

@ -82,7 +82,7 @@ class FuncSem[F[_]](val expr: FuncExpr[F]) extends AnyVal {
case (n, dt: DataType) => n -> Left(dt)
case (n, at: ArrowType) => n -> Right(at)
},
ret = retValue.map(ValuesAlgebra.valueToData),
ret = retValue.map(ValuesAlgebra.valueToData).flatMap(vd => funcArrow.res.map(vd -> _)),
body = bg
)