mirror of
https://github.com/fluencelabs/aqua.git
synced 2025-04-24 22:42:13 +00:00
39 fix callbacks (#43)
* Project layout reorganized * Topology resolution extracted to a separate file * FuncResolved class * aqua.model.transform * Basic bubbling * Get more context into topology resolver * Failing Topology test * Get back in seq * OnTag.via: List changed to Chain, as via is reversed and concatenated quite frequently
This commit is contained in:
parent
75595b99bd
commit
0de43b470e
@ -1,4 +1,4 @@
|
||||
package aqua.generator
|
||||
package aqua.backend.air
|
||||
|
||||
import cats.Show
|
||||
import cats.syntax.show._
|
||||
@ -81,7 +81,8 @@ object Air {
|
||||
|
||||
case class Match(left: DataView, right: DataView, instruction: Air) extends Air(Keyword.Match)
|
||||
|
||||
case class Mismatch(left: DataView, right: DataView, instruction: Air) extends Air(Keyword.Mismatch)
|
||||
case class Mismatch(left: DataView, right: DataView, instruction: Air)
|
||||
extends Air(Keyword.Mismatch)
|
||||
|
||||
case class Par(left: Air, right: Air) extends Air(Keyword.Par)
|
||||
|
||||
@ -89,7 +90,8 @@ object Air {
|
||||
|
||||
case class Xor(left: Air, right: Air) extends Air(Keyword.Xor)
|
||||
|
||||
case class Call(triplet: Triplet, args: List[DataView], result: Option[String]) extends Air(Keyword.Call)
|
||||
case class Call(triplet: Triplet, args: List[DataView], result: Option[String])
|
||||
extends Air(Keyword.Call)
|
||||
|
||||
private def show(depth: Int, air: Air): String = {
|
||||
def showNext(a: Air) = show(depth + 1, a)
|
||||
@ -101,7 +103,8 @@ object Air {
|
||||
case Air.Next(label) ⇒ s" $label"
|
||||
case Air.Fold(iter, label, inst) ⇒ s" ${iter.show} $label\n${showNext(inst)}$space"
|
||||
case Air.Match(left, right, inst) ⇒ s" ${left.show} ${right.show}\n${showNext(inst)}$space"
|
||||
case Air.Mismatch(left, right, inst) ⇒ s" ${left.show} ${right.show}\n${showNext(inst)}$space"
|
||||
case Air.Mismatch(left, right, inst) ⇒
|
||||
s" ${left.show} ${right.show}\n${showNext(inst)}$space"
|
||||
case Air.Par(l, r) ⇒ s"\n${showNext(l)}${showNext(r)}$space"
|
||||
case Air.Seq(l, r) ⇒ s"\n${showNext(l)}${showNext(r)}$space"
|
||||
case Air.Xor(l, r) ⇒ s"\n${showNext(l)}${showNext(r)}$space"
|
@ -1,23 +1,17 @@
|
||||
package aqua.generator
|
||||
package aqua.backend.air
|
||||
|
||||
import aqua.model.{
|
||||
import aqua.model._
|
||||
import aqua.model.body.{
|
||||
Call,
|
||||
CallArrowTag,
|
||||
CallServiceTag,
|
||||
ForTag,
|
||||
InitPeerIdModel,
|
||||
IntoArrayModel,
|
||||
IntoFieldModel,
|
||||
LambdaModel,
|
||||
LiteralModel,
|
||||
MatchMismatchTag,
|
||||
NextTag,
|
||||
OnTag,
|
||||
OpTag,
|
||||
ParTag,
|
||||
SeqTag,
|
||||
ValueModel,
|
||||
VarModel,
|
||||
XorTag
|
||||
}
|
||||
import cats.Eval
|
22
backend/air/src/main/scala/aqua/backend/air/FuncAirGen.scala
Normal file
22
backend/air/src/main/scala/aqua/backend/air/FuncAirGen.scala
Normal file
@ -0,0 +1,22 @@
|
||||
package aqua.backend.air
|
||||
|
||||
import aqua.model.FuncResolved
|
||||
import aqua.model.transform.BodyConfig
|
||||
|
||||
case class FuncAirGen(func: FuncResolved) {
|
||||
|
||||
/**
|
||||
* Generates AIR from the function body as it is, with no modifications and optimizations
|
||||
*/
|
||||
def generateAir: Air =
|
||||
AirGen(func.func.body.tree).generate
|
||||
|
||||
/**
|
||||
* Generates AIR from the optimized function body, assuming client is behind a relay
|
||||
* @return
|
||||
*/
|
||||
def generateClientAir(conf: BodyConfig = BodyConfig()): Air =
|
||||
AirGen(
|
||||
func.forClient(conf)
|
||||
).generate
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package aqua.backend.ts
|
||||
|
||||
import aqua.model.ScriptModel
|
||||
import aqua.model.transform.BodyConfig
|
||||
import cats.data.Chain
|
||||
|
||||
case class TypescriptFile(script: ScriptModel) {
|
||||
def funcs: Chain[TypescriptFunc] = script.resolveFunctions.map(TypescriptFunc(_))
|
||||
|
||||
def generateTS(conf: BodyConfig = BodyConfig()): String =
|
||||
TypescriptFile.Header + "\n\n" + funcs.map(_.generateTypescript(conf)).toList.mkString("\n\n")
|
||||
}
|
||||
|
||||
object TypescriptFile {
|
||||
|
||||
val Header: String =
|
||||
"""import { FluenceClient, PeerIdB58 } from '@fluencelabs/fluence';
|
||||
|import { RequestFlowBuilder } from '@fluencelabs/fluence/dist/api.unstable';
|
||||
|""".stripMargin
|
||||
|
||||
}
|
@ -1,42 +1,27 @@
|
||||
package aqua.generator
|
||||
package aqua.backend.ts
|
||||
|
||||
import aqua.model.FuncCallable
|
||||
import aqua.semantics.{ArrayType, ArrowType, DataType, LiteralType, ProductType, ScalarType, Type}
|
||||
import aqua.backend.air.FuncAirGen
|
||||
import aqua.model.FuncResolved
|
||||
import aqua.model.transform.BodyConfig
|
||||
import aqua.types._
|
||||
import cats.syntax.show._
|
||||
|
||||
case class TypescriptFunc(name: String, func: FuncCallable, tsAir: Air) {
|
||||
case class TypescriptFunc(func: FuncResolved) {
|
||||
|
||||
def typeToTs(t: Type): String = t match {
|
||||
case ArrayType(t) => typeToTs(t) + "[]"
|
||||
case pt: ProductType => s"{${pt.fields.map(typeToTs).toNel.map(kv => kv._1 + ":" + kv._2).toList.mkString(";")}}"
|
||||
case st: ScalarType if ScalarType.number(st) => "number"
|
||||
case ScalarType.bool => "boolean"
|
||||
case ScalarType.string => "string"
|
||||
case lt: LiteralType if lt.oneOf.exists(ScalarType.number) => "number"
|
||||
case lt: LiteralType if lt.oneOf(ScalarType.bool) => "boolean"
|
||||
case lt: LiteralType if lt.oneOf(ScalarType.string) => "string"
|
||||
case _: DataType => "any"
|
||||
case at: ArrowType =>
|
||||
s"(${argsToTs(at)}) => ${at.res
|
||||
.fold("void")(typeToTs)}"
|
||||
}
|
||||
|
||||
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(idx => s"args[$idx]").mkString(", ")
|
||||
import TypescriptFunc._
|
||||
|
||||
def argsTypescript: String =
|
||||
func.args.map {
|
||||
func.func.args.map {
|
||||
case (n, Left(t)) => s"${n}: " + typeToTs(t)
|
||||
case (n, Right(at)) => s"${n}: " + typeToTs(at)
|
||||
}.mkString(", ")
|
||||
|
||||
def generateTypescript: String = {
|
||||
def generateTypescript(conf: BodyConfig = BodyConfig()): String = {
|
||||
|
||||
val returnCallback = func.ret.map { case (dv, t) =>
|
||||
s"""h.on('${func.callbackService}', '${func.respFuncName}', (args) => {
|
||||
val tsAir = FuncAirGen(func).generateClientAir(conf)
|
||||
|
||||
val returnCallback = func.func.ret.map { case (dv, t) =>
|
||||
s"""h.on('${conf.callbackService}', '${conf.respFuncName}', (args) => {
|
||||
| const [res] = args;
|
||||
| resolve(res);
|
||||
|});
|
||||
@ -44,21 +29,21 @@ case class TypescriptFunc(name: String, func: FuncCallable, tsAir: Air) {
|
||||
|
||||
}
|
||||
|
||||
val setCallbacks = func.args.map {
|
||||
val setCallbacks = func.func.args.map {
|
||||
case (argName, Left(t)) =>
|
||||
s"""h.on('${func.getDataService}', '$argName', () => {return $argName;});"""
|
||||
s"""h.on('${conf.getDataService}', '$argName', () => {return $argName;});"""
|
||||
case (argName, Right(at)) =>
|
||||
s"""h.on('${func.callbackService}', '$argName', (args) => {return $argName(${argsCallToTs(
|
||||
s"""h.on('${conf.callbackService}', '$argName', (args) => {return $argName(${argsCallToTs(
|
||||
at
|
||||
)});});"""
|
||||
}.mkString("\n")
|
||||
|
||||
val retType = func.ret
|
||||
val retType = func.func.ret
|
||||
.map(_._2)
|
||||
.fold("void")(typeToTs)
|
||||
|
||||
s"""
|
||||
|export async function ${name}(client: FluenceClient${if (func.args.isEmpty) ""
|
||||
|export async function ${func.name}(client: FluenceClient${if (func.func.args.isEmpty) ""
|
||||
else ", "}${argsTypescript}): Promise<$retType> {
|
||||
| let request;
|
||||
| const promise = new Promise<$retType>((resolve, reject) => {
|
||||
@ -69,7 +54,7 @@ case class TypescriptFunc(name: String, func: FuncCallable, tsAir: Air) {
|
||||
| `,
|
||||
| )
|
||||
| .configHandler((h) => {
|
||||
| h.on('${func.getDataService}', 'relay', () => {
|
||||
| h.on('${conf.getDataService}', 'relay', () => {
|
||||
| return client.relayPeerId;
|
||||
| });
|
||||
| h.on('getRelayService', 'hasReleay', () => {// Not Used
|
||||
@ -96,3 +81,34 @@ case class TypescriptFunc(name: String, func: FuncCallable, tsAir: Air) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object TypescriptFunc {
|
||||
|
||||
def typeToTs(t: Type): String = t match {
|
||||
case ArrayType(t) => typeToTs(t) + "[]"
|
||||
case pt: ProductType =>
|
||||
s"{${pt.fields.map(typeToTs).toNel.map(kv => kv._1 + ":" + kv._2).toList.mkString(";")}}"
|
||||
case st: ScalarType if ScalarType.number(st) => "number"
|
||||
case ScalarType.bool => "boolean"
|
||||
case ScalarType.string => "string"
|
||||
case lt: LiteralType if lt.oneOf.exists(ScalarType.number) => "number"
|
||||
case lt: LiteralType if lt.oneOf(ScalarType.bool) => "boolean"
|
||||
case lt: LiteralType if lt.oneOf(ScalarType.string) => "string"
|
||||
case _: DataType => "any"
|
||||
case at: ArrowType =>
|
||||
s"(${argsToTs(at)}) => ${at.res
|
||||
.fold("void")(typeToTs)}"
|
||||
}
|
||||
|
||||
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(idx => s"args[$idx]").mkString(", ")
|
||||
|
||||
}
|
95
build.sbt
95
build.sbt
@ -1,29 +1,84 @@
|
||||
val dottyVersion = "2.13.5"
|
||||
|
||||
scalaVersion := dottyVersion
|
||||
|
||||
//val dottyVersion = "3.0.0-RC1"
|
||||
|
||||
val catsV = "2.4.2"
|
||||
val monocleV = "3.0.0-M3"
|
||||
val aquaV = "0.1.0"
|
||||
|
||||
lazy val root = project
|
||||
.in(file("."))
|
||||
val catsV = "2.4.2"
|
||||
val catsParseV = "0.3.1"
|
||||
val monocleV = "3.0.0-M3"
|
||||
val scalaTestV = "3.2.5"
|
||||
val fs2V = "3.0.0-M7"
|
||||
|
||||
name := "aqua-hll"
|
||||
mainClass in (Compile, run) := Some("aqua.Main")
|
||||
mainClass in assembly := Some("aqua.Main")
|
||||
assemblyJarName in assembly := "aqua-hll.jar"
|
||||
|
||||
val commons = Seq(
|
||||
version := aquaV,
|
||||
scalaVersion := dottyVersion,
|
||||
libraryDependencies += "org.scalatest" %% "scalatest" % scalaTestV % Test,
|
||||
addCompilerPlugin("org.typelevel" %% "kind-projector" % "0.11.3" cross CrossVersion.full)
|
||||
)
|
||||
|
||||
lazy val cli = project
|
||||
.settings(commons: _*)
|
||||
.settings(
|
||||
name := "aqua-hll",
|
||||
version := "0.1.0",
|
||||
scalaVersion := dottyVersion,
|
||||
mainClass in (Compile, run) := Some("aqua.Main"),
|
||||
mainClass in assembly := Some("aqua.Main"),
|
||||
assemblyJarName in assembly := "aqua-hll.jar",
|
||||
libraryDependencies ++= Seq(
|
||||
"com.github.scopt" %% "scopt" % "4.0.1",
|
||||
"org.typelevel" %% "cats-effect" % "3.0.0-RC2",
|
||||
"org.typelevel" %% "cats-parse" % "0.3.1",
|
||||
"org.typelevel" %% "cats-free" % catsV,
|
||||
"com.github.julien-truffaut" %% "monocle-core" % monocleV,
|
||||
"com.github.julien-truffaut" %% "monocle-macro" % monocleV,
|
||||
"co.fs2" %% "fs2-core" % "3.0.0-M7",
|
||||
"co.fs2" %% "fs2-io" % "3.0.0-M7",
|
||||
"org.scalatest" %% "scalatest" % "3.2.5" % Test
|
||||
"com.github.scopt" %% "scopt" % "4.0.1",
|
||||
"org.typelevel" %% "cats-effect" % "3.0.0-RC2",
|
||||
"co.fs2" %% "fs2-core" % fs2V,
|
||||
"co.fs2" %% "fs2-io" % fs2V
|
||||
)
|
||||
)
|
||||
.dependsOn(semantics, `backend-air`, `backend-ts`)
|
||||
|
||||
lazy val types = project
|
||||
.settings(commons)
|
||||
.settings(
|
||||
libraryDependencies ++= Seq(
|
||||
"org.typelevel" %% "cats-core" % catsV
|
||||
)
|
||||
)
|
||||
|
||||
addCompilerPlugin("org.typelevel" %% "kind-projector" % "0.11.3" cross CrossVersion.full)
|
||||
lazy val parser = project
|
||||
.settings(commons: _*)
|
||||
.settings(
|
||||
libraryDependencies ++= Seq(
|
||||
"org.typelevel" %% "cats-parse" % catsParseV,
|
||||
"org.typelevel" %% "cats-free" % catsV
|
||||
)
|
||||
)
|
||||
.dependsOn(types)
|
||||
|
||||
lazy val model = project
|
||||
.settings(commons: _*)
|
||||
.settings(
|
||||
libraryDependencies ++= Seq(
|
||||
"org.typelevel" %% "cats-free" % catsV
|
||||
)
|
||||
)
|
||||
.dependsOn(types)
|
||||
|
||||
lazy val semantics = project
|
||||
.settings(commons: _*)
|
||||
.settings(
|
||||
libraryDependencies ++= Seq(
|
||||
"com.github.julien-truffaut" %% "monocle-core" % monocleV,
|
||||
"com.github.julien-truffaut" %% "monocle-macro" % monocleV
|
||||
)
|
||||
)
|
||||
.dependsOn(model, parser)
|
||||
|
||||
lazy val `backend-air` = project
|
||||
.in(file("backend/air"))
|
||||
.settings(commons: _*)
|
||||
.dependsOn(model)
|
||||
|
||||
lazy val `backend-ts` = project
|
||||
.in(file("backend/ts"))
|
||||
.settings(commons: _*)
|
||||
.dependsOn(`backend-air`)
|
||||
|
@ -1,15 +1,18 @@
|
||||
package aqua
|
||||
|
||||
import aqua.backend.air.FuncAirGen
|
||||
import aqua.backend.ts.TypescriptFile
|
||||
import aqua.model.{Model, ScriptModel}
|
||||
import aqua.parser.Ast
|
||||
import cats.data.ValidatedNec
|
||||
import aqua.parser.lift.Span
|
||||
import aqua.semantics.Semantics
|
||||
import cats.syntax.show._
|
||||
|
||||
object Aqua {
|
||||
|
||||
def parse(input: String): ValidatedNec[AquaError, Ast[Span.F]] =
|
||||
Ast.fromString[Span.F](input)
|
||||
Ast.fromString[Span.F](input).leftMap(_.map(pe => SyntaxError(pe.failedAtOffset, pe.expected)))
|
||||
|
||||
def generateModel(input: String): ValidatedNec[AquaError, Model] =
|
||||
parse(input).andThen(ast =>
|
||||
@ -18,8 +21,20 @@ object Aqua {
|
||||
|
||||
def generate(input: String, air: Boolean): ValidatedNec[AquaError, String] =
|
||||
generateModel(input).map {
|
||||
case m: ScriptModel if air =>
|
||||
// TODO it's meaningless to compile all functions to air, as resulting .air file is incorrect; only one function should be taken
|
||||
m.resolveFunctions
|
||||
.map(FuncAirGen)
|
||||
.map(g =>
|
||||
// add function name before body
|
||||
s";; function name: ${g.func.name}\n\n" + g.generateAir.show
|
||||
)
|
||||
.toList
|
||||
.mkString("\n\n\n")
|
||||
|
||||
case m: ScriptModel =>
|
||||
if (air) m.generateAir else m.generateTypescript
|
||||
TypescriptFile(m).generateTS()
|
||||
|
||||
case _ => "//No input given"
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package aqua.model
|
||||
|
||||
import aqua.semantics.{ArrowType, DataType, Type}
|
||||
import aqua.model.body.{Call, CallArrowTag, FuncOp, OpTag}
|
||||
import aqua.types.{ArrowType, DataType, Type}
|
||||
import cats.Eval
|
||||
import cats.data.Chain
|
||||
import cats.free.Cofree
|
||||
@ -94,97 +95,4 @@ case class FuncCallable(
|
||||
}
|
||||
}
|
||||
|
||||
val getDataService: String = "getDataSrv"
|
||||
val callbackService: String = "callbackSrv"
|
||||
|
||||
val respFuncName = "response"
|
||||
val relayVarName = "relay"
|
||||
|
||||
val callbackId: ValueModel = LiteralModel("\"" + callbackService + "\"")
|
||||
|
||||
val returnCallback: Option[FuncOp] = ret.map { case (dv, t) =>
|
||||
viaRelay(
|
||||
FuncOp.leaf(
|
||||
CallServiceTag(
|
||||
callbackId,
|
||||
respFuncName,
|
||||
Call(
|
||||
(dv, t) :: Nil,
|
||||
None
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// TODO it's an overkill
|
||||
def initPeerCallable(name: String, arrowType: ArrowType): FuncCallable =
|
||||
FuncCallable(
|
||||
viaRelay(
|
||||
FuncOp.leaf(
|
||||
CallServiceTag(
|
||||
callbackId,
|
||||
name,
|
||||
Call(
|
||||
arrowType.args.zipWithIndex.map { case (t, i) =>
|
||||
VarModel(s"arg$i") -> t
|
||||
},
|
||||
arrowType.res.map(_ => "init_call_res")
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
arrowType.args.zipWithIndex.map {
|
||||
case (t: DataType, i) => s"arg$i" -> Left(t)
|
||||
case (t: ArrowType, i) => s"arg$i" -> Right(t)
|
||||
},
|
||||
arrowType.res.map(VarModel("init_call_res") -> _),
|
||||
Map.empty
|
||||
)
|
||||
|
||||
// TODO rename
|
||||
def generateTsModel: FuncOp =
|
||||
FuncOp
|
||||
.node(
|
||||
SeqTag,
|
||||
Chain
|
||||
.fromSeq(
|
||||
args.collect { case (argName, Left(_)) =>
|
||||
getDataOp(argName)
|
||||
} :+ getDataOp(relayVarName)
|
||||
)
|
||||
.append(
|
||||
apply(
|
||||
generateTsCall,
|
||||
args.collect { case (argName, Right(arrowType)) =>
|
||||
argName -> initPeerCallable(argName, arrowType)
|
||||
}.toMap,
|
||||
args.collect { case (argName, Left(_)) =>
|
||||
argName
|
||||
}.foldLeft(Set(relayVarName))(_ + _)
|
||||
).value._1
|
||||
) ++ Chain.fromSeq(returnCallback.toSeq)
|
||||
)
|
||||
.resolveTopology()
|
||||
|
||||
def generateTsCall: Call =
|
||||
Call(
|
||||
args.map { case (k, e) =>
|
||||
(VarModel(k), e.fold(identity, identity))
|
||||
},
|
||||
None
|
||||
)
|
||||
|
||||
def getDataOp(name: String): FuncOp =
|
||||
FuncOp.leaf(
|
||||
CallServiceTag(
|
||||
LiteralModel("\"" + getDataService + "\""),
|
||||
name,
|
||||
Call(Nil, Some(name))
|
||||
)
|
||||
)
|
||||
|
||||
def viaRelay(op: FuncOp): FuncOp =
|
||||
FuncOp.wrap(OnTag(InitPeerIdModel, VarModel(relayVarName) :: Nil), op)
|
||||
|
||||
}
|
16
model/src/main/scala/aqua/model/FuncModel.scala
Normal file
16
model/src/main/scala/aqua/model/FuncModel.scala
Normal file
@ -0,0 +1,16 @@
|
||||
package aqua.model
|
||||
|
||||
import aqua.model.body.FuncOp
|
||||
import aqua.types.{ArrowType, DataType, Type}
|
||||
|
||||
case class FuncModel(
|
||||
name: String,
|
||||
args: List[(String, Either[DataType, ArrowType])],
|
||||
ret: Option[(ValueModel, Type)],
|
||||
body: FuncOp
|
||||
) extends Model {
|
||||
|
||||
def captureArrows(arrows: Map[String, FuncCallable]): FuncCallable =
|
||||
FuncCallable(body, args, ret, arrows)
|
||||
|
||||
}
|
10
model/src/main/scala/aqua/model/FuncResolved.scala
Normal file
10
model/src/main/scala/aqua/model/FuncResolved.scala
Normal file
@ -0,0 +1,10 @@
|
||||
package aqua.model
|
||||
|
||||
import aqua.model.body.OpTag
|
||||
import aqua.model.transform.{BodyConfig, ForClient}
|
||||
import cats.data.Chain
|
||||
import cats.free.Cofree
|
||||
|
||||
case class FuncResolved(name: String, func: FuncCallable) {
|
||||
def forClient(conf: BodyConfig): Cofree[Chain, OpTag] = ForClient(this, conf)
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package aqua.model
|
||||
|
||||
import aqua.model.body.FuncOp
|
||||
import cats.data.Chain
|
||||
import cats.kernel.Semigroup
|
||||
|
21
model/src/main/scala/aqua/model/ScriptModel.scala
Normal file
21
model/src/main/scala/aqua/model/ScriptModel.scala
Normal file
@ -0,0 +1,21 @@
|
||||
package aqua.model
|
||||
|
||||
import cats.data.Chain
|
||||
|
||||
case class ScriptModel(funcs: Chain[FuncModel]) extends Model {
|
||||
|
||||
def enqueue(m: Model): ScriptModel = m match {
|
||||
case f: FuncModel => copy(funcs.append(f))
|
||||
case _ => this
|
||||
}
|
||||
|
||||
def resolveFunctions: Chain[FuncResolved] =
|
||||
funcs
|
||||
.foldLeft((Map.empty[String, FuncCallable], Chain.empty[FuncResolved])) {
|
||||
case ((funcsAcc, outputAcc), func) =>
|
||||
val fr = func.captureArrows(funcsAcc)
|
||||
funcsAcc.updated(func.name, fr) -> outputAcc.append(FuncResolved(func.name, fr))
|
||||
}
|
||||
._2
|
||||
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package aqua.model
|
||||
package aqua.model.body
|
||||
|
||||
import aqua.model.{LiteralModel, Model, ValueModel, VarModel}
|
||||
import cats.Eval
|
||||
import cats.data.Chain
|
||||
import cats.free.Cofree
|
||||
@ -42,28 +43,6 @@ case class FuncOp(tree: Cofree[Chain, OpTag]) extends Model {
|
||||
)
|
||||
)
|
||||
|
||||
def resolveTopology(): FuncOp =
|
||||
FuncOp(FuncOp.transformWithPath(tree) {
|
||||
case (path, c: CallServiceTag) =>
|
||||
Cofree[Chain, OpTag](
|
||||
c.copy(peerId = path.collectFirst { case OnTag(peerId, _) =>
|
||||
peerId
|
||||
}),
|
||||
Eval.now(Chain.empty)
|
||||
)
|
||||
case (_, OnTag(pid, via)) if via.nonEmpty =>
|
||||
Cofree[Chain, OpTag](
|
||||
OnTag(pid, Nil),
|
||||
Eval.now(
|
||||
Chain.fromSeq(
|
||||
via.map(FuncOp.noop).map(_.tree)
|
||||
)
|
||||
)
|
||||
)
|
||||
case (_, t) =>
|
||||
Cofree[Chain, OpTag](t, Eval.now(Chain.empty))
|
||||
})
|
||||
|
||||
def :+:(prev: FuncOp): FuncOp =
|
||||
FuncOp.RightAssocSemi.combine(prev, this)
|
||||
}
|
||||
@ -71,10 +50,7 @@ case class FuncOp(tree: Cofree[Chain, OpTag]) extends Model {
|
||||
object FuncOp {
|
||||
|
||||
def noop(peerId: ValueModel): FuncOp =
|
||||
FuncOp.wrap(
|
||||
OnTag(peerId, Nil),
|
||||
FuncOp.leaf(CallServiceTag(LiteralModel("\"op\""), "identity", Call(Nil, None), Some(peerId)))
|
||||
)
|
||||
FuncOp.leaf(CallServiceTag(LiteralModel("\"op\""), "identity", Call(Nil, None), Some(peerId)))
|
||||
|
||||
def traverseA[A](cf: Cofree[Chain, OpTag], init: A)(
|
||||
f: (A, OpTag) => (A, Cofree[Chain, OpTag])
|
||||
@ -89,19 +65,7 @@ object FuncOp {
|
||||
.map(_.map(ch => head.copy(tail = Eval.now(ch))))
|
||||
}
|
||||
|
||||
def transformWithPath(cf: Cofree[Chain, OpTag], path: List[OpTag] = Nil)(
|
||||
f: (List[OpTag], OpTag) => Cofree[Chain, OpTag]
|
||||
): Cofree[Chain, OpTag] = {
|
||||
val newCf = f(path, cf.head)
|
||||
Cofree[Chain, OpTag](
|
||||
newCf.head,
|
||||
(newCf.tail, cf.tail)
|
||||
.mapN(_ ++ _)
|
||||
// IF make foldLeft here, will be possible to get info from prev sibling
|
||||
.map(_.map(transformWithPath(_, newCf.head :: path)(f)))
|
||||
)
|
||||
}
|
||||
|
||||
// Semigroup for foldRight processing
|
||||
object RightAssocSemi extends Semigroup[FuncOp] {
|
||||
|
||||
override def combine(x: FuncOp, y: FuncOp): FuncOp = (x.tree.head, y.tree.head) match {
|
@ -1,6 +1,8 @@
|
||||
package aqua.model
|
||||
package aqua.model.body
|
||||
|
||||
import aqua.semantics.Type
|
||||
import aqua.model.{AbilityModel, ValueModel}
|
||||
import aqua.types.Type
|
||||
import cats.data.Chain
|
||||
|
||||
case class Call(args: List[(ValueModel, Type)], exportTo: Option[String]) {
|
||||
|
||||
@ -19,7 +21,8 @@ sealed trait OpTag {
|
||||
|
||||
def mapValues(f: ValueModel => ValueModel): OpTag = this match {
|
||||
case OnTag(peerId, via) => OnTag(f(peerId), via.map(f))
|
||||
case MatchMismatchTag(left, right, shouldMatch) => MatchMismatchTag(f(left), f(right), shouldMatch)
|
||||
case MatchMismatchTag(left, right, shouldMatch) =>
|
||||
MatchMismatchTag(f(left), f(right), shouldMatch)
|
||||
case ForTag(item, iterable) => ForTag(item, f(iterable))
|
||||
case CallArrowTag(ability, funcName, call) =>
|
||||
CallArrowTag(
|
||||
@ -42,7 +45,7 @@ sealed trait OpTag {
|
||||
case object SeqTag extends OpTag
|
||||
case object ParTag extends OpTag
|
||||
case object XorTag extends OpTag
|
||||
case class OnTag(peerId: ValueModel, via: List[ValueModel]) extends OpTag
|
||||
case class OnTag(peerId: ValueModel, via: Chain[ValueModel]) extends OpTag
|
||||
case class NextTag(item: String) extends OpTag
|
||||
case class MatchMismatchTag(left: ValueModel, right: ValueModel, shouldMatch: Boolean) extends OpTag
|
||||
case class ForTag(item: String, iterable: ValueModel) extends OpTag
|
15
model/src/main/scala/aqua/model/transform/BodyConfig.scala
Normal file
15
model/src/main/scala/aqua/model/transform/BodyConfig.scala
Normal file
@ -0,0 +1,15 @@
|
||||
package aqua.model.transform
|
||||
|
||||
import aqua.model.{LiteralModel, ValueModel}
|
||||
|
||||
case class BodyConfig(
|
||||
getDataService: String = "getDataSrv",
|
||||
callbackService: String = "callbackSrv",
|
||||
respFuncName: String = "response",
|
||||
relayVarName: String = "relay"
|
||||
) {
|
||||
|
||||
val callbackSrvId: ValueModel = LiteralModel("\"" + callbackService + "\"")
|
||||
val dataSrvId: ValueModel = LiteralModel("\"" + getDataService + "\"")
|
||||
|
||||
}
|
107
model/src/main/scala/aqua/model/transform/ForClient.scala
Normal file
107
model/src/main/scala/aqua/model/transform/ForClient.scala
Normal file
@ -0,0 +1,107 @@
|
||||
package aqua.model.transform
|
||||
|
||||
import aqua.model.body._
|
||||
import aqua.model.{FuncCallable, FuncResolved, InitPeerIdModel, VarModel}
|
||||
import aqua.types.{ArrowType, DataType}
|
||||
import cats.data.Chain
|
||||
import cats.free.Cofree
|
||||
|
||||
object ForClient {
|
||||
|
||||
def apply(func: FuncResolved, conf: BodyConfig): Cofree[Chain, OpTag] = {
|
||||
import conf._
|
||||
|
||||
// Get to init user through a relay
|
||||
def viaRelay(op: FuncOp): FuncOp =
|
||||
FuncOp.wrap(OnTag(InitPeerIdModel, Chain.one(VarModel(relayVarName))), op)
|
||||
|
||||
val returnCallback: Option[FuncOp] = func.func.ret.map { case (dv, t) =>
|
||||
viaRelay(
|
||||
FuncOp.leaf(
|
||||
CallServiceTag(
|
||||
callbackSrvId,
|
||||
respFuncName,
|
||||
Call(
|
||||
(dv, t) :: Nil,
|
||||
None
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// TODO it's an overkill, is it?
|
||||
def initPeerCallable(name: String, arrowType: ArrowType): FuncCallable =
|
||||
FuncCallable(
|
||||
viaRelay(
|
||||
FuncOp.leaf(
|
||||
CallServiceTag(
|
||||
callbackSrvId,
|
||||
name,
|
||||
Call(
|
||||
arrowType.args.zipWithIndex.map { case (t, i) =>
|
||||
VarModel(s"arg$i") -> t
|
||||
},
|
||||
arrowType.res.map(_ => "init_call_res")
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
arrowType.args.zipWithIndex.map {
|
||||
case (t: DataType, i) => s"arg$i" -> Left(t)
|
||||
case (t: ArrowType, i) => s"arg$i" -> Right(t)
|
||||
},
|
||||
arrowType.res.map(VarModel("init_call_res") -> _),
|
||||
Map.empty
|
||||
)
|
||||
|
||||
// Like it is called from TS
|
||||
def funcArgsCall: Call =
|
||||
Call(
|
||||
func.func.args.map { case (k, e) =>
|
||||
(VarModel(k), e.fold(identity, identity))
|
||||
},
|
||||
None
|
||||
)
|
||||
|
||||
// Get data with this name from a local service
|
||||
def getDataOp(name: String): FuncOp =
|
||||
FuncOp.leaf(
|
||||
CallServiceTag(
|
||||
dataSrvId,
|
||||
name,
|
||||
Call(Nil, Some(name))
|
||||
)
|
||||
)
|
||||
|
||||
val body =
|
||||
viaRelay(
|
||||
FuncOp
|
||||
.node(
|
||||
SeqTag,
|
||||
Chain
|
||||
.fromSeq(
|
||||
func.func.args.collect { case (argName, Left(_)) =>
|
||||
getDataOp(argName)
|
||||
} :+ getDataOp(relayVarName)
|
||||
)
|
||||
.append(
|
||||
func.func
|
||||
.apply(
|
||||
funcArgsCall,
|
||||
func.func.args.collect { case (argName, Right(arrowType)) =>
|
||||
argName -> initPeerCallable(argName, arrowType)
|
||||
}.toMap,
|
||||
func.func.args.collect { case (argName, Left(_)) =>
|
||||
argName
|
||||
}.foldLeft(Set(relayVarName))(_ + _)
|
||||
)
|
||||
.value
|
||||
._1
|
||||
) ++ Chain.fromSeq(returnCallback.toSeq)
|
||||
)
|
||||
).tree
|
||||
|
||||
Topology.resolve(body)
|
||||
}
|
||||
}
|
78
model/src/main/scala/aqua/model/transform/Topology.scala
Normal file
78
model/src/main/scala/aqua/model/transform/Topology.scala
Normal file
@ -0,0 +1,78 @@
|
||||
package aqua.model.transform
|
||||
|
||||
import aqua.model.ValueModel
|
||||
import aqua.model.body.{CallServiceTag, FuncOp, OnTag, OpTag, SeqTag}
|
||||
import cats.Eval
|
||||
import cats.data.Chain
|
||||
import cats.free.Cofree
|
||||
|
||||
object Topology {
|
||||
type Tree = Cofree[Chain, OpTag]
|
||||
|
||||
def through(peers: Chain[ValueModel]): Chain[Tree] =
|
||||
peers.map(FuncOp.noop).map(_.tree)
|
||||
|
||||
// TODO: after topology is resolved, OnTag should be eliminated
|
||||
def resolve(op: Tree): Tree =
|
||||
transformWithPath(op) {
|
||||
case (path, c: CallServiceTag, children) if c.peerId.isEmpty =>
|
||||
Cofree[Chain, OpTag](
|
||||
c.copy(peerId = path.collectFirst { case OnTag(peerId, _) =>
|
||||
peerId
|
||||
}),
|
||||
children
|
||||
)
|
||||
case (path, tag @ OnTag(pid, via), children) =>
|
||||
// Drop seq/par/xor from path
|
||||
val pathOn = path.collect { case ot: OnTag =>
|
||||
ot
|
||||
}
|
||||
|
||||
pathOn match {
|
||||
// If we are on the right node, do nothing
|
||||
case Nil =>
|
||||
Cofree[Chain, OpTag](tag, children)
|
||||
case h :: _ if h.peerId == pid =>
|
||||
Cofree[Chain, OpTag](tag, children)
|
||||
case h :: _ =>
|
||||
Cofree[Chain, OpTag](
|
||||
tag,
|
||||
// TODO: merge children, if possible
|
||||
children.map(through(h.via.reverse ++ via) ++ _)
|
||||
)
|
||||
}
|
||||
|
||||
case (path, SeqTag, children) =>
|
||||
// TODO if we have OnTag, and then something else, need to get back
|
||||
// AND keep in mind that we will handle all the children with OnTag processor!
|
||||
def modifyChildrenList(list: List[Tree]): Chain[Tree] = list match {
|
||||
case Nil => Chain.empty
|
||||
case op :: Nil =>
|
||||
// TODO: it is a last op, and it could be an On tag; in this case, get back?
|
||||
Chain.one(op)
|
||||
case (oncf @ Cofree(ont: OnTag, _)) :: op :: tail =>
|
||||
(oncf +: through(ont.via.reverse ++ Chain.fromSeq(path.collectFirst { case t: OnTag =>
|
||||
t.via.toList
|
||||
}.toList.flatten))) ++ modifyChildrenList(op :: tail)
|
||||
case o :: ops => o +: modifyChildrenList(ops)
|
||||
}
|
||||
|
||||
Cofree[Chain, OpTag](
|
||||
SeqTag,
|
||||
children.map(_.toList).map(modifyChildrenList)
|
||||
)
|
||||
|
||||
case (_, t, children) =>
|
||||
Cofree[Chain, OpTag](t, children)
|
||||
}
|
||||
|
||||
def transformWithPath(cf: Tree, path: List[OpTag] = Nil)(
|
||||
f: (List[OpTag], OpTag, Eval[Chain[Tree]]) => Tree
|
||||
): Tree = {
|
||||
val newCf = f(path, cf.head, cf.tail)
|
||||
Cofree[Chain, OpTag](
|
||||
newCf.head,
|
||||
newCf.tail.map(_.map(transformWithPath(_, newCf.head :: path)(f)))
|
||||
)
|
||||
}
|
||||
}
|
48
model/src/test/scala/aqua/model/Node.scala
Normal file
48
model/src/test/scala/aqua/model/Node.scala
Normal file
@ -0,0 +1,48 @@
|
||||
package aqua.model
|
||||
|
||||
import aqua.model.body.{Call, CallServiceTag, FuncOp, OnTag, OpTag, SeqTag}
|
||||
import cats.Eval
|
||||
import cats.data.Chain
|
||||
import cats.free.Cofree
|
||||
|
||||
import scala.language.implicitConversions
|
||||
|
||||
// Helper to simplify building and visualizing Cofree structures
|
||||
case class Node(tag: OpTag, ops: List[Node] = Nil) {
|
||||
|
||||
override def toString: String =
|
||||
tag.toString + (if (ops.isEmpty) "\n" else s"{\n${ops.mkString}\n}\n")
|
||||
}
|
||||
|
||||
object Node {
|
||||
type Cof = Cofree[Chain, OpTag]
|
||||
|
||||
implicit def cofToNode(cof: Cof): Node =
|
||||
Node(cof.head, cof.tailForced.toList.map(cofToNode))
|
||||
|
||||
implicit def nodeToCof(tree: Node): Cof =
|
||||
Cofree(tree.tag, Eval.later(Chain.fromSeq(tree.ops.map(nodeToCof))))
|
||||
|
||||
val relay = LiteralModel("relay")
|
||||
val initPeer = InitPeerIdModel
|
||||
val emptyCall = Call(Nil, None)
|
||||
val otherPeer = LiteralModel("other-peer")
|
||||
val otherRelay = LiteralModel("other-relay")
|
||||
val otherPeer2 = LiteralModel("other-peer-2")
|
||||
val otherRelay2 = LiteralModel("other-relay-2")
|
||||
|
||||
def call(i: Int, on: ValueModel = null) = Node(
|
||||
CallServiceTag(LiteralModel(s"srv$i"), s"fn$i", Call(Nil, None), Option(on))
|
||||
)
|
||||
|
||||
def seq(nodes: Node*) = Node(SeqTag, nodes.toList)
|
||||
|
||||
def on(peer: ValueModel, via: List[ValueModel], body: Node*) =
|
||||
Node(
|
||||
OnTag(peer, Chain.fromSeq(via)),
|
||||
body.toList
|
||||
)
|
||||
|
||||
def through(peer: ValueModel): Node =
|
||||
FuncOp.noop(peer).tree
|
||||
}
|
147
model/src/test/scala/aqua/model/transform/TopologySpec.scala
Normal file
147
model/src/test/scala/aqua/model/transform/TopologySpec.scala
Normal file
@ -0,0 +1,147 @@
|
||||
package aqua.model.transform
|
||||
|
||||
import aqua.model.Node
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
class TopologySpec extends AnyFlatSpec with Matchers {
|
||||
import Node._
|
||||
|
||||
"topology resolver" should "do nothing on init peer" in {
|
||||
|
||||
val init = on(
|
||||
initPeer,
|
||||
relay :: Nil,
|
||||
seq(
|
||||
call(1),
|
||||
call(2)
|
||||
)
|
||||
)
|
||||
|
||||
val proc: Node = Topology.resolve(init)
|
||||
|
||||
val expected = on(
|
||||
initPeer,
|
||||
relay :: Nil,
|
||||
seq(
|
||||
call(1, initPeer),
|
||||
call(2, initPeer)
|
||||
)
|
||||
)
|
||||
|
||||
proc should be(expected)
|
||||
|
||||
}
|
||||
|
||||
"topology resolver" should "go through relay to any other node, directly" in {
|
||||
|
||||
val init = on(
|
||||
initPeer,
|
||||
relay :: Nil,
|
||||
on(
|
||||
otherPeer,
|
||||
Nil,
|
||||
seq(
|
||||
call(1),
|
||||
call(2)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val proc: Node = Topology.resolve(init)
|
||||
|
||||
val expected = on(
|
||||
initPeer,
|
||||
relay :: Nil,
|
||||
on(
|
||||
otherPeer,
|
||||
Nil,
|
||||
through(relay),
|
||||
seq(
|
||||
call(1, otherPeer),
|
||||
call(2, otherPeer)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
proc should be(expected)
|
||||
}
|
||||
|
||||
"topology resolver" should "go through relay to any other node, via another relay" in {
|
||||
|
||||
val init = on(
|
||||
initPeer,
|
||||
relay :: Nil,
|
||||
on(
|
||||
otherPeer,
|
||||
otherRelay :: Nil,
|
||||
seq(
|
||||
call(1),
|
||||
call(2)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val proc: Node = Topology.resolve(init)
|
||||
|
||||
val expected = on(
|
||||
initPeer,
|
||||
relay :: Nil,
|
||||
on(
|
||||
otherPeer,
|
||||
otherRelay :: Nil,
|
||||
through(relay),
|
||||
through(otherRelay),
|
||||
seq(
|
||||
call(1, otherPeer),
|
||||
call(2, otherPeer)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
proc should be(expected)
|
||||
}
|
||||
|
||||
"topology resolver" should "get back to init peer" in {
|
||||
|
||||
val init = on(
|
||||
initPeer,
|
||||
relay :: Nil,
|
||||
seq(
|
||||
on(
|
||||
otherPeer,
|
||||
otherRelay :: Nil,
|
||||
call(1)
|
||||
),
|
||||
call(2)
|
||||
)
|
||||
)
|
||||
|
||||
val proc: Node = Topology.resolve(init)
|
||||
|
||||
val expected = on(
|
||||
initPeer,
|
||||
relay :: Nil,
|
||||
seq(
|
||||
on(
|
||||
otherPeer,
|
||||
otherRelay :: Nil,
|
||||
through(relay),
|
||||
through(otherRelay),
|
||||
call(1, otherPeer)
|
||||
),
|
||||
through(otherRelay),
|
||||
through(relay),
|
||||
call(2, initPeer)
|
||||
)
|
||||
)
|
||||
|
||||
// println(Console.BLUE + init)
|
||||
// println(Console.YELLOW + proc)
|
||||
// println(Console.MAGENTA + expected)
|
||||
// println(Console.RESET)
|
||||
|
||||
proc should be(expected)
|
||||
}
|
||||
|
||||
}
|
@ -3,7 +3,6 @@ package aqua.parser
|
||||
import aqua.parser.expr._
|
||||
import aqua.parser.lexer.Token._
|
||||
import aqua.parser.lift.LiftParser
|
||||
import aqua.{AquaError, SyntaxError}
|
||||
import cats.data.{Chain, NonEmptyChain, Validated, ValidatedNec}
|
||||
import cats.free.Cofree
|
||||
import cats.parse.{Parser => P, Parser0 => P0}
|
||||
@ -29,10 +28,10 @@ object Ast {
|
||||
.surroundedBy(` \n+`.?)
|
||||
.map(ls => Ast(Cofree(RootExpr(), Eval.now(ls))))
|
||||
|
||||
def fromString[F[_]: LiftParser: Comonad](script: String): ValidatedNec[AquaError, Ast[F]] =
|
||||
def fromString[F[_]: LiftParser: Comonad](script: String): ValidatedNec[P.Error, Ast[F]] =
|
||||
Validated
|
||||
.fromEither(
|
||||
parser[F](Indent()).parseAll(script)
|
||||
)
|
||||
.leftMap(pe => NonEmptyChain.one(SyntaxError(pe.failedAtOffset, pe.expected)))
|
||||
.leftMap(NonEmptyChain.one)
|
||||
}
|
@ -5,12 +5,13 @@ import aqua.parser.lexer.{EqOp, Literal, Value}
|
||||
import cats.parse.{Parser => P}
|
||||
import aqua.parser.lexer.Token._
|
||||
import aqua.parser.lift.LiftParser
|
||||
import aqua.semantics.LiteralType
|
||||
import aqua.types.LiteralType
|
||||
import cats.Comonad
|
||||
|
||||
case class IfExpr[F[_]](left: Value[F], eqOp: EqOp[F], right: Value[F]) extends Expr[F]
|
||||
|
||||
object IfExpr extends Expr.AndIndented(Expr.defer(OnExpr), ParExpr, CallArrowExpr, AbilityIdExpr, ForExpr) {
|
||||
object IfExpr
|
||||
extends Expr.AndIndented(Expr.defer(OnExpr), ParExpr, CallArrowExpr, AbilityIdExpr, ForExpr) {
|
||||
|
||||
override def p[F[_]: LiftParser: Comonad]: P[IfExpr[F]] =
|
||||
(`if` *> ` ` *> Value.`value`[F] ~ (` ` *> EqOp.p[F] ~ (` ` *> Value.`value`[F])).?).map {
|
@ -3,8 +3,8 @@ package aqua.parser.lexer
|
||||
import aqua.parser.lexer.Token._
|
||||
import aqua.parser.lift.LiftParser
|
||||
import aqua.parser.lift.LiftParser._
|
||||
import aqua.semantics.ScalarType
|
||||
import cats.{Comonad, Functor}
|
||||
import aqua.types.ScalarType
|
||||
import cats.Comonad
|
||||
import cats.parse.{Parser => P}
|
||||
import cats.syntax.functor._
|
||||
import cats.syntax.comonad._
|
||||
@ -12,7 +12,8 @@ import cats.syntax.comonad._
|
||||
sealed trait TypeToken[F[_]] extends Token[F]
|
||||
sealed trait DataTypeToken[F[_]] extends TypeToken[F]
|
||||
|
||||
case class ArrayTypeToken[F[_]: Comonad](override val unit: F[Unit], data: DataTypeToken[F]) extends DataTypeToken[F] {
|
||||
case class ArrayTypeToken[F[_]: Comonad](override val unit: F[Unit], data: DataTypeToken[F])
|
||||
extends DataTypeToken[F] {
|
||||
override def as[T](v: T): F[T] = unit.as(v)
|
||||
}
|
||||
|
||||
@ -68,7 +69,8 @@ object ArrowTypeToken {
|
||||
}
|
||||
}
|
||||
|
||||
case class AquaArrowType[F[_]](args: List[TypeToken[F]], res: Option[DataTypeToken[F]]) extends ArrowDef[F] {
|
||||
case class AquaArrowType[F[_]](args: List[TypeToken[F]], res: Option[DataTypeToken[F]])
|
||||
extends ArrowDef[F] {
|
||||
override def argTypes: List[TypeToken[F]] = args
|
||||
|
||||
override def resType: Option[DataTypeToken[F]] = res
|
||||
@ -80,7 +82,9 @@ object DataTypeToken {
|
||||
(P.string("[]").lift ~ `datatypedef`[F]).map(ud => ArrayTypeToken(ud._1, ud._2))
|
||||
|
||||
def `datatypedef`[F[_]: LiftParser: Comonad]: P[DataTypeToken[F]] =
|
||||
P.oneOf(P.defer(`arraytypedef`[F]) :: BasicTypeToken.`basictypedef`[F] :: CustomTypeToken.ct[F] :: Nil)
|
||||
P.oneOf(
|
||||
P.defer(`arraytypedef`[F]) :: BasicTypeToken.`basictypedef`[F] :: CustomTypeToken.ct[F] :: Nil
|
||||
)
|
||||
}
|
||||
|
||||
object TypeToken {
|
@ -3,7 +3,7 @@ package aqua.parser.lexer
|
||||
import aqua.parser.lexer.Token._
|
||||
import aqua.parser.lift.LiftParser
|
||||
import aqua.parser.lift.LiftParser._
|
||||
import aqua.semantics.LiteralType
|
||||
import aqua.types.LiteralType
|
||||
import cats.{Comonad, Functor}
|
||||
import cats.parse.{Numbers, Parser => P}
|
||||
import cats.syntax.functor._
|
||||
@ -43,7 +43,9 @@ object Value {
|
||||
)
|
||||
|
||||
def float[F[_]: LiftParser: Comonad]: P[Literal[F]] =
|
||||
(P.char('-').?.with1 ~ (Numbers.nonNegativeIntString <* P.char('.')) ~ Numbers.nonNegativeIntString).string.lift
|
||||
(P.char('-').?.with1 ~ (Numbers.nonNegativeIntString <* P.char(
|
||||
'.'
|
||||
)) ~ Numbers.nonNegativeIntString).string.lift
|
||||
.map(Literal(_, LiteralType.float))
|
||||
|
||||
// TODO make more sophisticated escaping/unescaping
|
||||
@ -51,8 +53,10 @@ object Value {
|
||||
(`"` *> P.charsWhile0(_ != '"') <* `"`).string.lift
|
||||
.map(Literal(_, LiteralType.string))
|
||||
|
||||
def literal[F[_]: LiftParser: Comonad]: P[Literal[F]] = P.oneOf(bool :: float.backtrack :: num :: string :: Nil)
|
||||
def literal[F[_]: LiftParser: Comonad]: P[Literal[F]] =
|
||||
P.oneOf(bool :: float.backtrack :: num :: string :: Nil)
|
||||
|
||||
def `value`[F[_]: LiftParser: Comonad]: P[Value[F]] = P.oneOf(literal.backtrack :: varLambda :: Nil)
|
||||
def `value`[F[_]: LiftParser: Comonad]: P[Value[F]] =
|
||||
P.oneOf(literal.backtrack :: varLambda :: Nil)
|
||||
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package aqua
|
||||
|
||||
import aqua.model.Call
|
||||
import aqua.parser.expr.{
|
||||
AbilityIdExpr,
|
||||
AliasExpr,
|
||||
@ -34,14 +33,14 @@ import aqua.parser.lexer.{
|
||||
}
|
||||
import cats.Id
|
||||
import aqua.parser.lift.LiftParser.Implicits.idLiftParser
|
||||
import aqua.semantics.LiteralType.{bool, number, string}
|
||||
import aqua.semantics.{LiteralType, ScalarType}
|
||||
import aqua.types.LiteralType.{bool, number, string}
|
||||
import aqua.types.{LiteralType, ScalarType}
|
||||
import org.scalatest.EitherValues
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.language.implicitConversions
|
||||
|
||||
object Utils {
|
||||
object AquaSpec {
|
||||
implicit def toAb(str: String): Ability[Id] = Ability[Id](str)
|
||||
|
||||
implicit def toName(str: String): Name[Id] = Name[Id](str)
|
||||
@ -85,9 +84,7 @@ object Utils {
|
||||
val stringSc: BasicTypeToken[Id] = BasicTypeToken[Id](ScalarType.string)
|
||||
}
|
||||
|
||||
trait Utils extends EitherValues {
|
||||
|
||||
val emptyCall = Call(Nil, None)
|
||||
trait AquaSpec extends EitherValues {
|
||||
|
||||
def parseExpr(str: String): CallArrowExpr[Id] =
|
||||
CallArrowExpr.p[Id].parseAll(str).value
|
@ -1,15 +1,15 @@
|
||||
package aqua.parser
|
||||
|
||||
import aqua.Utils
|
||||
import aqua.AquaSpec
|
||||
import aqua.parser.expr.AbilityIdExpr
|
||||
import aqua.parser.lexer.Literal
|
||||
import aqua.semantics.LiteralType
|
||||
import aqua.types.LiteralType
|
||||
import cats.Id
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
class AbilitIdExprSpec extends AnyFlatSpec with Matchers with Utils {
|
||||
import Utils._
|
||||
class AbilitIdExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
import AquaSpec._
|
||||
|
||||
"abilities" should "be parsed" in {
|
||||
parseAbId("Ab a") should be(
|
@ -1,15 +1,15 @@
|
||||
package aqua.parser
|
||||
|
||||
import aqua.Utils
|
||||
import aqua.AquaSpec
|
||||
import aqua.parser.expr.AliasExpr
|
||||
import aqua.semantics.ScalarType.u32
|
||||
import aqua.types.ScalarType.u32
|
||||
import cats.Id
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
class AliasExprSpec extends AnyFlatSpec with Matchers with Utils {
|
||||
class AliasExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
|
||||
import Utils._
|
||||
import AquaSpec._
|
||||
|
||||
"alias" should "be parsed properly" in {
|
||||
parseAlias("alias SomeAlias : u32") should be(
|
@ -1,16 +1,16 @@
|
||||
package aqua.parser
|
||||
|
||||
import aqua.Utils
|
||||
import aqua.AquaSpec
|
||||
import aqua.parser.expr.ArrowTypeExpr
|
||||
import aqua.semantics.ScalarType.string
|
||||
import aqua.semantics.ScalarType.u32
|
||||
import aqua.types.ScalarType.string
|
||||
import aqua.types.ScalarType.u32
|
||||
import cats.Id
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
class ArrowTypeExprSpec extends AnyFlatSpec with Matchers with Utils {
|
||||
class ArrowTypeExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
|
||||
import Utils._
|
||||
import AquaSpec._
|
||||
|
||||
"arrow types" should "be parsed properly" in {
|
||||
parseArrow("onIn: string -> ()") should be(
|
||||
@ -22,7 +22,10 @@ class ArrowTypeExprSpec extends AnyFlatSpec with Matchers with Utils {
|
||||
)
|
||||
|
||||
parseArrow("onIn: Custom, string, u32, Custom3 -> Custom2") should be(
|
||||
ArrowTypeExpr[Id]("onIn", toArrowType(List("Custom", string, u32, "Custom3"), Some("Custom2")))
|
||||
ArrowTypeExpr[Id](
|
||||
"onIn",
|
||||
toArrowType(List("Custom", string, u32, "Custom3"), Some("Custom2"))
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
@ -1,19 +1,24 @@
|
||||
package aqua.parser
|
||||
|
||||
import aqua.Utils
|
||||
import aqua.AquaSpec
|
||||
import aqua.parser.expr.CallArrowExpr
|
||||
import aqua.parser.lexer.{Name, VarLambda}
|
||||
import cats.Id
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
class CallArrowSpec extends AnyFlatSpec with Matchers with Utils {
|
||||
import Utils._
|
||||
class CallArrowSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
import AquaSpec._
|
||||
|
||||
"func calls" should "parse func()" in {
|
||||
parseExpr("func()") should be(CallArrowExpr[Id](None, None, toName("func"), List()))
|
||||
parseExpr("Ab.func(arg)") should be(
|
||||
CallArrowExpr[Id](None, Some(toAb("Ab")), Name[Id]("func"), List(VarLambda[Id](toName("arg"))))
|
||||
CallArrowExpr[Id](
|
||||
None,
|
||||
Some(toAb("Ab")),
|
||||
Name[Id]("func"),
|
||||
List(VarLambda[Id](toName("arg")))
|
||||
)
|
||||
)
|
||||
|
||||
parseExpr("func(arg.doSomething)") should be(
|
||||
@ -34,7 +39,10 @@ class CallArrowSpec extends AnyFlatSpec with Matchers with Utils {
|
||||
None,
|
||||
Some(toAb("Ab")),
|
||||
Name[Id]("func"),
|
||||
List(toVarLambda("arg", List("doSomething", "and", "doSomethingElse")), toVarLambda("arg2", List("someFunc")))
|
||||
List(
|
||||
toVarLambda("arg", List("doSomething", "and", "doSomethingElse")),
|
||||
toVarLambda("arg2", List("someFunc"))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -1,15 +1,15 @@
|
||||
package aqua.parser
|
||||
|
||||
import aqua.Utils
|
||||
import aqua.AquaSpec
|
||||
import aqua.parser.expr.{AliasExpr, DataStructExpr}
|
||||
import aqua.semantics.ScalarType.u32
|
||||
import aqua.types.ScalarType.u32
|
||||
import cats.Id
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
class DataStructExprSpec extends AnyFlatSpec with Matchers with Utils {
|
||||
class DataStructExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
|
||||
import Utils._
|
||||
import AquaSpec._
|
||||
|
||||
"data struct" should "be parsed properly" in {
|
||||
parseDataStruct("data Smth") should be(
|
@ -1,14 +1,14 @@
|
||||
package aqua.parser
|
||||
|
||||
import aqua.Utils
|
||||
import aqua.AquaSpec
|
||||
import aqua.parser.expr.{ElseOtherwiseExpr, OnExpr}
|
||||
import aqua.semantics.LiteralType.{number, string}
|
||||
import aqua.types.LiteralType.{number, string}
|
||||
import cats.Id
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
class ElseOtherwiseExprSpec extends AnyFlatSpec with Matchers with Utils {
|
||||
import Utils._
|
||||
class ElseOtherwiseExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
import AquaSpec._
|
||||
|
||||
"else" should "be parsed" in {
|
||||
parseElse("else") should be(
|
@ -1,14 +1,14 @@
|
||||
package aqua.parser
|
||||
|
||||
import aqua.Utils
|
||||
import aqua.AquaSpec
|
||||
import aqua.parser.expr.{ElseOtherwiseExpr, FieldTypeExpr}
|
||||
import aqua.semantics.ScalarType.bool
|
||||
import aqua.types.ScalarType.bool
|
||||
import cats.Id
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
class FieldTypeExprSpec extends AnyFlatSpec with Matchers with Utils {
|
||||
import Utils._
|
||||
class FieldTypeExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
import AquaSpec._
|
||||
|
||||
"else" should "be parsed" in {
|
||||
parseFieldType("some: bool") should be(
|
@ -1,13 +1,13 @@
|
||||
package aqua.parser
|
||||
|
||||
import aqua.Utils
|
||||
import aqua.AquaSpec
|
||||
import aqua.parser.expr.ForExpr
|
||||
import cats.Id
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
class ForExprSpec extends AnyFlatSpec with Matchers with Utils {
|
||||
import Utils._
|
||||
class ForExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
import AquaSpec._
|
||||
|
||||
"on" should "be parsed" in {
|
||||
parseFor("for some <- \"a\"") should be(
|
@ -1,49 +1,23 @@
|
||||
package aqua.parser
|
||||
|
||||
import aqua.model.{
|
||||
Call,
|
||||
CallArrowTag,
|
||||
CallServiceTag,
|
||||
FuncCallable,
|
||||
LiteralModel,
|
||||
OnTag,
|
||||
OpTag,
|
||||
ScriptModel,
|
||||
SeqTag,
|
||||
XorTag
|
||||
}
|
||||
import aqua.{SyntaxError, Utils}
|
||||
import aqua.parser.Ast.{parser, Tree}
|
||||
import aqua.parser.expr.{
|
||||
AbilityIdExpr,
|
||||
ArrowTypeExpr,
|
||||
CallArrowExpr,
|
||||
FuncExpr,
|
||||
IfExpr,
|
||||
OnExpr,
|
||||
ReturnExpr,
|
||||
RootExpr,
|
||||
ServiceExpr
|
||||
}
|
||||
import aqua.AquaSpec
|
||||
import aqua.parser.Ast.parser
|
||||
import aqua.parser.expr._
|
||||
import aqua.parser.lexer.{ArrowTypeToken, BasicTypeToken, EqOp}
|
||||
import aqua.semantics.ScalarType.{bool, u64}
|
||||
import cats.{Eval, Id}
|
||||
import cats.syntax.traverse._
|
||||
import aqua.parser.lift.LiftParser.Implicits.idLiftParser
|
||||
import aqua.types.ScalarType.{bool, string, u32, u64}
|
||||
import cats.Id
|
||||
import cats.data.Chain
|
||||
import cats.free.Cofree
|
||||
import cats.syntax.foldable._
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import aqua.parser.lift.LiftParser.Implicits.idLiftParser
|
||||
import aqua.semantics.Semantics
|
||||
import aqua.semantics.Semantics.{folder, Alg}
|
||||
import cats.data.{Chain, NonEmptyChain, Validated}
|
||||
import cats.free.Cofree
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.language.implicitConversions
|
||||
|
||||
class FuncExprSpec extends AnyFlatSpec with Matchers with Utils {
|
||||
import Utils._
|
||||
import aqua.semantics.ScalarType.{string, u32}
|
||||
class FuncExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
import AquaSpec._
|
||||
|
||||
"func header" should "parse" in {
|
||||
funcExpr("func some() -> bool") should be(
|
||||
@ -130,7 +104,7 @@ class FuncExprSpec extends AnyFlatSpec with Matchers with Utils {
|
||||
|
||||
}
|
||||
|
||||
"some" should "other" in {
|
||||
"multi function expression" should "parse" in {
|
||||
val script =
|
||||
"""service Local("local"):
|
||||
| gt: -> bool
|
||||
@ -168,6 +142,7 @@ class FuncExprSpec extends AnyFlatSpec with Matchers with Utils {
|
||||
qTree.d() shouldBe CallArrowExpr(Some("two"), None, "tryGen", List())
|
||||
qTree.d() shouldBe CallArrowExpr(Some("three"), Some(toAb("Local")), "gt", List())
|
||||
qTree.d() shouldBe ReturnExpr(toVar("two"))
|
||||
/* TODO this is semantics, not parser test
|
||||
|
||||
val f =
|
||||
Semantics.generateModel(tree).toList.head.asInstanceOf[ScriptModel]
|
||||
@ -260,6 +235,6 @@ class FuncExprSpec extends AnyFlatSpec with Matchers with Utils {
|
||||
// return to 'else' node
|
||||
qGenComplex.d() shouldBe elseOn
|
||||
qGenComplex.d() shouldBe elseCall
|
||||
qGenComplex.d() shouldBe CallServiceTag(local, "gt", Call(List(), Some("three")), None)
|
||||
qGenComplex.d() shouldBe CallServiceTag(local, "gt", Call(List(), Some("three")), None)*/
|
||||
}
|
||||
}
|
@ -1,16 +1,15 @@
|
||||
package aqua.parser
|
||||
|
||||
import aqua.Utils
|
||||
import aqua.parser.expr.{ForExpr, IfExpr}
|
||||
import aqua.AquaSpec
|
||||
import aqua.parser.expr.IfExpr
|
||||
import aqua.parser.lexer.EqOp
|
||||
import aqua.semantics.LiteralType.{bool, number, string}
|
||||
import cats.Id
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
class IfExprSpec extends AnyFlatSpec with Matchers with Utils {
|
||||
class IfExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
|
||||
import Utils._
|
||||
import AquaSpec._
|
||||
|
||||
"if" should "be parsed" in {
|
||||
parseIf("if a") should be(
|
@ -1,14 +1,13 @@
|
||||
package aqua.parser
|
||||
|
||||
import aqua.Utils
|
||||
import aqua.AquaSpec
|
||||
import aqua.parser.expr.OnExpr
|
||||
import aqua.semantics.LiteralType.{number, string}
|
||||
import cats.Id
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
class OnExprSpec extends AnyFlatSpec with Matchers with Utils {
|
||||
import Utils._
|
||||
class OnExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
import AquaSpec._
|
||||
|
||||
"on" should "be parsed" in {
|
||||
parseOn("on peer") should be(
|
@ -1,14 +1,13 @@
|
||||
package aqua.parser
|
||||
|
||||
import aqua.Utils
|
||||
import aqua.AquaSpec
|
||||
import aqua.parser.expr.{OnExpr, ParExpr}
|
||||
import aqua.semantics.LiteralType.{number, string}
|
||||
import cats.Id
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
class ParExprSpec extends AnyFlatSpec with Matchers with Utils {
|
||||
import Utils._
|
||||
class ParExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
import AquaSpec._
|
||||
|
||||
"on" should "be parsed" in {
|
||||
parsePar("par") should be(
|
@ -1,13 +1,13 @@
|
||||
package aqua.parser
|
||||
|
||||
import aqua.Utils
|
||||
import aqua.AquaSpec
|
||||
import aqua.parser.expr.{ParExpr, ReturnExpr}
|
||||
import cats.Id
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
class ReturnExprSpec extends AnyFlatSpec with Matchers with Utils {
|
||||
import Utils._
|
||||
class ReturnExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
import AquaSpec._
|
||||
|
||||
"on" should "be parsed" in {
|
||||
parseReturn("<- true") should be(
|
@ -1,14 +1,14 @@
|
||||
package aqua.parser
|
||||
|
||||
import aqua.Utils
|
||||
import aqua.AquaSpec
|
||||
import aqua.parser.expr.{ReturnExpr, ServiceExpr}
|
||||
import cats.Id
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
class ServiceExprSpec extends AnyFlatSpec with Matchers with Utils {
|
||||
class ServiceExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
|
||||
|
||||
import Utils._
|
||||
import AquaSpec._
|
||||
|
||||
"on" should "be parsed" in {
|
||||
parseService("service Local(\"local\")") should be(
|
@ -1,11 +1,11 @@
|
||||
package aqua.parser.lexer
|
||||
|
||||
import aqua.parser.lift.LiftParser.Implicits.idLiftParser
|
||||
import cats.Id
|
||||
import cats.data.NonEmptyList
|
||||
import org.scalatest.EitherValues
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import cats.Id
|
||||
import aqua.parser.lift.LiftParser.Implicits.idLiftParser
|
||||
import cats.data.NonEmptyList
|
||||
|
||||
class LambdaOpSpec extends AnyFlatSpec with Matchers with EitherValues {
|
||||
|
||||
@ -14,7 +14,9 @@ class LambdaOpSpec extends AnyFlatSpec with Matchers with EitherValues {
|
||||
|
||||
opsP(".field") should be(NonEmptyList.of(IntoField[Id]("field")))
|
||||
opsP(".field.sub") should be(NonEmptyList.of(IntoField[Id]("field"), IntoField[Id]("sub")))
|
||||
opsP(".field*.sub") should be(NonEmptyList.of(IntoField[Id]("field"), IntoArray[Id](()), IntoField[Id]("sub")))
|
||||
opsP(".field*.sub") should be(
|
||||
NonEmptyList.of(IntoField[Id]("field"), IntoArray[Id](()), IntoField[Id]("sub"))
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -2,10 +2,10 @@ package aqua.parser.lexer
|
||||
|
||||
import aqua.parser.lexer.Token._
|
||||
import cats.data.NonEmptyList
|
||||
import cats.parse.{Parser => P}
|
||||
import org.scalatest.EitherValues
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import cats.parse.{Parser => P}
|
||||
|
||||
class TokenSpec extends AnyFlatSpec with Matchers with EitherValues {
|
||||
|
||||
@ -49,9 +49,15 @@ class TokenSpec extends AnyFlatSpec with Matchers with EitherValues {
|
||||
"indented" should "parse 1 or more lines" in {
|
||||
indented(_ => `.`, "").parseAll(" .\n .").right.value should be(NonEmptyList.of((), ()))
|
||||
indented(_ => `.`, "").parseAll(" .\n .\n .").right.value should be(NonEmptyList.of((), (), ()))
|
||||
indented(_ => `.`, "").parse(" .\n .\n .\n").right.value should be(("\n", NonEmptyList.of((), (), ())))
|
||||
indented(_ => `.`, "").parse(" .\n .\n .\n ").right.value should be(("\n ", NonEmptyList.of((), (), ())))
|
||||
indented(_ => `.`, "").parse(" .\n .\n .\n .").right.value should be(("", NonEmptyList.of((), (), (), ())))
|
||||
indented(_ => `.`, "").parse(" .\n .\n .\n").right.value should be(
|
||||
("\n", NonEmptyList.of((), (), ()))
|
||||
)
|
||||
indented(_ => `.`, "").parse(" .\n .\n .\n ").right.value should be(
|
||||
("\n ", NonEmptyList.of((), (), ()))
|
||||
)
|
||||
indented(_ => `.`, "").parse(" .\n .\n .\n .").right.value should be(
|
||||
("", NonEmptyList.of((), (), (), ()))
|
||||
)
|
||||
indented(_ => `.`, "").parseAll(" .").right.value should be(NonEmptyList.of(()))
|
||||
|
||||
indented(_ => `.`, " ").parse(" .\n .").right.value should be(("\n .", NonEmptyList.of(())))
|
||||
@ -64,7 +70,10 @@ class TokenSpec extends AnyFlatSpec with Matchers with EitherValues {
|
||||
case class Node(branches: NonEmptyList[Tree]) extends Tree
|
||||
|
||||
lazy val p: P[NonEmptyList[Tree]] =
|
||||
indented(_ => P.string("newline") *> (` \n+` *> P.defer(p)).?.map(_.fold[Tree](Leaf)(Node)), "")
|
||||
indented(
|
||||
_ => P.string("newline") *> (` \n+` *> P.defer(p)).?.map(_.fold[Tree](Leaf)(Node)),
|
||||
""
|
||||
)
|
||||
p.parseAll(" newline").right.value should be(NonEmptyList.of(Leaf))
|
||||
}
|
||||
|
@ -4,14 +4,13 @@ import org.scalatest.EitherValues
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import aqua.parser.lift.LiftParser.Implicits.idLiftParser
|
||||
import aqua.semantics.ScalarType
|
||||
import aqua.types.ScalarType
|
||||
import aqua.types.ScalarType.u32
|
||||
import cats.Id
|
||||
|
||||
import scala.language.implicitConversions
|
||||
|
||||
class TypeSpec extends AnyFlatSpec with Matchers with EitherValues {
|
||||
|
||||
import ScalarType.u32
|
||||
class TypeTokenSpec extends AnyFlatSpec with Matchers with EitherValues {
|
||||
|
||||
implicit def strToBt(st: ScalarType): BasicTypeToken[Id] = BasicTypeToken[Id](st)
|
||||
|
||||
@ -44,7 +43,9 @@ class TypeSpec extends AnyFlatSpec with Matchers with EitherValues {
|
||||
ArrowTypeToken.`arrowdef`.parseAll("[]Absolutely, u32 -> B").right.value should be(
|
||||
ArrowTypeToken[Id](
|
||||
(),
|
||||
ArrayTypeToken[Id]((), CustomTypeToken[Id]("Absolutely")) :: (u32: BasicTypeToken[Id]) :: Nil,
|
||||
ArrayTypeToken[Id]((), CustomTypeToken[Id]("Absolutely")) :: (u32: BasicTypeToken[
|
||||
Id
|
||||
]) :: Nil,
|
||||
Some(CustomTypeToken[Id]("B"))
|
||||
)
|
||||
)
|
||||
@ -55,7 +56,9 @@ class TypeSpec extends AnyFlatSpec with Matchers with EitherValues {
|
||||
TypeToken.`typedef`.parseAll("[]Something") should be(
|
||||
Right(ArrayTypeToken[Id]((), CustomTypeToken[Id]("Something")))
|
||||
)
|
||||
TypeToken.`typedef`.parseAll("[]u32") should be(Right(ArrayTypeToken[Id]((), u32: BasicTypeToken[Id])))
|
||||
TypeToken.`typedef`.parseAll("[]u32") should be(
|
||||
Right(ArrayTypeToken[Id]((), u32: BasicTypeToken[Id]))
|
||||
)
|
||||
TypeToken.`typedef`.parseAll("[][]u32") should be(
|
||||
Right(ArrayTypeToken[Id]((), ArrayTypeToken[Id]((), u32: BasicTypeToken[Id])))
|
||||
)
|
@ -4,7 +4,7 @@ import org.scalatest.EitherValues
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import aqua.parser.lift.LiftParser.Implicits.idLiftParser
|
||||
import aqua.semantics.LiteralType
|
||||
import aqua.types.LiteralType
|
||||
import cats.Id
|
||||
|
||||
class ValueSpec extends AnyFlatSpec with Matchers with EitherValues {
|
@ -1,6 +1,7 @@
|
||||
package aqua.semantics
|
||||
|
||||
import aqua.model.{FuncOp, Model}
|
||||
import aqua.model.Model
|
||||
import aqua.model.body.FuncOp
|
||||
import aqua.parser.lexer.Token
|
||||
import aqua.parser.{Ast, Expr}
|
||||
import aqua.semantics.rules.ReportError
|
@ -1,12 +1,14 @@
|
||||
package aqua.semantics.expr
|
||||
|
||||
import aqua.model.{Call, CallArrowTag, CallServiceTag, FuncOp, Model, ValueModel}
|
||||
import aqua.model.body.{Call, CallArrowTag, CallServiceTag, FuncOp}
|
||||
import aqua.model.{Model, ValueModel}
|
||||
import aqua.parser.expr.CallArrowExpr
|
||||
import aqua.semantics.{ArrowType, Prog, Type}
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.ValuesAlgebra
|
||||
import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
||||
import aqua.semantics.rules.names.NamesAlgebra
|
||||
import aqua.semantics.rules.types.TypesAlgebra
|
||||
import aqua.types.{ArrowType, Type}
|
||||
import cats.free.Free
|
||||
import cats.syntax.flatMap._
|
||||
import cats.syntax.functor._
|
||||
@ -20,7 +22,10 @@ class CallArrowSem[F[_]](val expr: CallArrowExpr[F]) extends AnyVal {
|
||||
|
||||
private def checkArgsRes[Alg[_]](
|
||||
at: ArrowType
|
||||
)(implicit N: NamesAlgebra[F, Alg], V: ValuesAlgebra[F, Alg]): Free[Alg, List[(ValueModel, Type)]] =
|
||||
)(implicit
|
||||
N: NamesAlgebra[F, Alg],
|
||||
V: ValuesAlgebra[F, Alg]
|
||||
): Free[Alg, List[(ValueModel, Type)]] =
|
||||
V.checkArguments(expr.funcName, at, args) >> variable
|
||||
.fold(freeUnit[Alg])(exportVar =>
|
||||
at.res.fold(
|
@ -1,6 +1,7 @@
|
||||
package aqua.semantics.expr
|
||||
|
||||
import aqua.model.{FuncOp, Model, XorTag}
|
||||
import aqua.model.Model
|
||||
import aqua.model.body.{FuncOp, XorTag}
|
||||
import aqua.parser.expr.ElseOtherwiseExpr
|
||||
import aqua.semantics.Prog
|
||||
import cats.free.Free
|
@ -1,11 +1,13 @@
|
||||
package aqua.semantics.expr
|
||||
|
||||
import aqua.model.{ForTag, FuncOp, Model, NextTag, OpTag, ParTag, SeqTag}
|
||||
import aqua.model.Model
|
||||
import aqua.model.body.{ForTag, FuncOp, NextTag, OpTag, ParTag, SeqTag}
|
||||
import aqua.parser.expr.ForExpr
|
||||
import aqua.semantics.{ArrayType, DataType, Prog}
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.ValuesAlgebra
|
||||
import aqua.semantics.rules.names.NamesAlgebra
|
||||
import aqua.semantics.rules.types.TypesAlgebra
|
||||
import aqua.types.{ArrayType, DataType}
|
||||
import cats.data.Chain
|
||||
import cats.free.Free
|
||||
import cats.syntax.flatMap._
|
||||
@ -31,7 +33,10 @@ class ForSem[F[_]](val expr: ForExpr[F]) extends AnyVal {
|
||||
case op: FuncOp =>
|
||||
FuncOp.wrap(
|
||||
ForTag(expr.item.value, ValuesAlgebra.valueToModel(expr.iterable)),
|
||||
FuncOp.node(expr.par.fold[OpTag](SeqTag)(_ => ParTag), Chain(op, FuncOp.leaf(NextTag(expr.item.value))))
|
||||
FuncOp.node(
|
||||
expr.par.fold[OpTag](SeqTag)(_ => ParTag),
|
||||
Chain(op, FuncOp.leaf(NextTag(expr.item.value)))
|
||||
)
|
||||
)
|
||||
case _ => Model.error("Wrong body of For expr")
|
||||
})
|
@ -1,13 +1,15 @@
|
||||
package aqua.semantics.expr
|
||||
|
||||
import aqua.model.{FuncModel, FuncOp, Model}
|
||||
import aqua.model.body.FuncOp
|
||||
import aqua.model.{FuncModel, Model}
|
||||
import aqua.parser.expr.FuncExpr
|
||||
import aqua.parser.lexer.Arg
|
||||
import aqua.semantics.{ArrowType, DataType, Prog, Type}
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.ValuesAlgebra
|
||||
import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
||||
import aqua.semantics.rules.names.NamesAlgebra
|
||||
import aqua.semantics.rules.types.TypesAlgebra
|
||||
import aqua.types.{ArrowType, DataType, Type}
|
||||
import cats.Applicative
|
||||
import cats.data.Chain
|
||||
import cats.free.Free
|
@ -1,6 +1,7 @@
|
||||
package aqua.semantics.expr
|
||||
|
||||
import aqua.model.{FuncOp, MatchMismatchTag, Model}
|
||||
import aqua.model.Model
|
||||
import aqua.model.body.{FuncOp, MatchMismatchTag}
|
||||
import aqua.parser.expr.IfExpr
|
||||
import aqua.semantics.rules.ValuesAlgebra
|
||||
import aqua.semantics.rules.types.TypesAlgebra
|
@ -1,6 +1,7 @@
|
||||
package aqua.semantics.expr
|
||||
|
||||
import aqua.model.{FuncOp, Model, OnTag}
|
||||
import aqua.model.Model
|
||||
import aqua.model.body.{FuncOp, OnTag}
|
||||
import aqua.parser.expr.OnExpr
|
||||
import aqua.semantics.Prog
|
||||
import aqua.semantics.rules.ValuesAlgebra
|
||||
@ -25,28 +26,12 @@ class OnSem[F[_]](val expr: OnExpr[F]) extends AnyVal {
|
||||
(_: Unit, ops: Model) =>
|
||||
A.endScope() as (ops match {
|
||||
case op: FuncOp =>
|
||||
// the way to the node may be lost if there will be chains of `on` without calls
|
||||
// so peerId is added to the path
|
||||
val path = expr.via :+ expr.peerId
|
||||
val returnPath = path.reverse
|
||||
val returnLeaf = {
|
||||
FuncOp.leaf(
|
||||
OnTag(
|
||||
ValuesAlgebra.valueToModel(returnPath.last),
|
||||
returnPath.map(ValuesAlgebra.valueToModel)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
FuncOp.node(
|
||||
FuncOp.wrap(
|
||||
OnTag(
|
||||
ValuesAlgebra.valueToModel(expr.peerId),
|
||||
path.map(ValuesAlgebra.valueToModel)
|
||||
Chain.fromSeq(expr.via).map(ValuesAlgebra.valueToModel)
|
||||
),
|
||||
Chain(
|
||||
op,
|
||||
returnLeaf
|
||||
)
|
||||
op
|
||||
)
|
||||
|
||||
case m => Model.error("On body is not an op, it's " + m)
|
@ -1,6 +1,7 @@
|
||||
package aqua.semantics.expr
|
||||
|
||||
import aqua.model.{FuncOp, Model, ParTag}
|
||||
import aqua.model.Model
|
||||
import aqua.model.body.{FuncOp, ParTag}
|
||||
import aqua.parser.expr.ParExpr
|
||||
import aqua.semantics.Prog
|
||||
import cats.free.Free
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user