mirror of
https://github.com/fluencelabs/aqua.git
synced 2025-04-24 22:42:13 +00:00
Fix via path, complex tests (#27)
This commit is contained in:
parent
cad921a958
commit
992af16a08
@ -2,7 +2,7 @@ version = 2.7.5
|
|||||||
|
|
||||||
docstrings = JavaDoc
|
docstrings = JavaDoc
|
||||||
|
|
||||||
maxColumn = 120
|
maxColumn = 100
|
||||||
|
|
||||||
align = none
|
align = none
|
||||||
align {
|
align {
|
||||||
|
@ -11,13 +11,15 @@ object Aqua {
|
|||||||
def parse(input: String): ValidatedNec[AquaError, Ast[Span.F]] =
|
def parse(input: String): ValidatedNec[AquaError, Ast[Span.F]] =
|
||||||
Ast.fromString[Span.F](input)
|
Ast.fromString[Span.F](input)
|
||||||
|
|
||||||
def validate(input: String): ValidatedNec[AquaError, Model] =
|
def generateModel(input: String): ValidatedNec[AquaError, Model] =
|
||||||
parse(input).andThen(ast => Semantics.validate(ast).leftMap(_.map(ts => CompilerError(ts._1.unit._1, ts._2))))
|
parse(input).andThen(ast =>
|
||||||
|
Semantics.generateModel(ast).leftMap(_.map(ts => CompilerError(ts._1.unit._1, ts._2)))
|
||||||
|
)
|
||||||
|
|
||||||
def generate(input: String, air: Boolean): ValidatedNec[AquaError, String] =
|
def generate(input: String, air: Boolean): ValidatedNec[AquaError, String] =
|
||||||
validate(input).map {
|
generateModel(input).map {
|
||||||
case g: ScriptModel =>
|
case m: ScriptModel =>
|
||||||
if (air) g.generateAir else g.generateTypescript
|
if (air) m.generateAir else m.generateTypescript
|
||||||
case _ => "//No input given"
|
case _ => "//No input given"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,8 @@ case class FuncCallable(
|
|||||||
// We have some names in scope (forbiddenNames), can't introduce them again; so find new names
|
// We have some names in scope (forbiddenNames), can't introduce them again; so find new names
|
||||||
val shouldRename = findNewNames(forbiddenNames, treeDefines)
|
val shouldRename = findNewNames(forbiddenNames, treeDefines)
|
||||||
// If there was a collision, rename exports and usages with new names
|
// If there was a collision, rename exports and usages with new names
|
||||||
val treeRenamed = if (shouldRename.isEmpty) treeWithValues else treeWithValues.rename(shouldRename)
|
val treeRenamed =
|
||||||
|
if (shouldRename.isEmpty) treeWithValues else treeWithValues.rename(shouldRename)
|
||||||
|
|
||||||
// Result could be derived from arguments, or renamed; take care about that
|
// Result could be derived from arguments, or renamed; take care about that
|
||||||
val result = ret.map(_._1).map(_.resolveWith(argsToData)).map {
|
val result = ret.map(_._1).map(_.resolveWith(argsToData)).map {
|
||||||
@ -71,7 +72,9 @@ case class FuncCallable(
|
|||||||
case ((noNames, resolvedExports), CallArrowTag(None, fn, c)) if allArrows.contains(fn) =>
|
case ((noNames, resolvedExports), CallArrowTag(None, fn, c)) if allArrows.contains(fn) =>
|
||||||
// Apply arguments to a function – recursion
|
// Apply arguments to a function – recursion
|
||||||
val (appliedOp, value) =
|
val (appliedOp, value) =
|
||||||
allArrows(fn).apply(c.mapValues(_.resolveWith(resolvedExports)), argsToArrows, noNames).value
|
allArrows(fn)
|
||||||
|
.apply(c.mapValues(_.resolveWith(resolvedExports)), argsToArrows, noNames)
|
||||||
|
.value
|
||||||
|
|
||||||
// Function defines new names inside its body – need to collect them
|
// Function defines new names inside its body – need to collect them
|
||||||
// TODO: actually it's done and dropped – so keep and pass it instead
|
// TODO: actually it's done and dropped – so keep and pass it instead
|
||||||
@ -80,7 +83,10 @@ case class FuncCallable(
|
|||||||
(noNames ++ newNames, resolvedExports ++ c.exportTo.zip(value)) -> appliedOp.tree
|
(noNames ++ newNames, resolvedExports ++ c.exportTo.zip(value)) -> appliedOp.tree
|
||||||
case (acc @ (_, resolvedExports), tag) =>
|
case (acc @ (_, resolvedExports), tag) =>
|
||||||
// All the other tags are already resolved and need no substitution
|
// All the other tags are already resolved and need no substitution
|
||||||
acc -> Cofree[Chain, OpTag](tag.mapValues(_.resolveWith(resolvedExports)), Eval.now(Chain.empty))
|
acc -> Cofree[Chain, OpTag](
|
||||||
|
tag.mapValues(_.resolveWith(resolvedExports)),
|
||||||
|
Eval.now(Chain.empty)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
.map { case ((_, resolvedExports), b) =>
|
.map { case ((_, resolvedExports), b) =>
|
||||||
// If return value is affected by any of internal functions, resolve it
|
// If return value is affected by any of internal functions, resolve it
|
||||||
@ -159,13 +165,7 @@ case class FuncCallable(
|
|||||||
).value._1
|
).value._1
|
||||||
) ++ Chain.fromSeq(returnCallback.toSeq)
|
) ++ Chain.fromSeq(returnCallback.toSeq)
|
||||||
)
|
)
|
||||||
.resolveTopology(noop)
|
.resolveTopology()
|
||||||
|
|
||||||
def noop(peerId: ValueModel): FuncOp =
|
|
||||||
FuncOp.wrap(
|
|
||||||
OnTag(peerId, Nil),
|
|
||||||
FuncOp.leaf(CallServiceTag(LiteralModel("\"op\""), "identity", Call(Nil, None), Some(peerId)))
|
|
||||||
)
|
|
||||||
|
|
||||||
def generateTsCall: Call =
|
def generateTsCall: Call =
|
||||||
Call(
|
Call(
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package aqua.model
|
package aqua.model
|
||||||
|
|
||||||
import aqua.model.FuncOp.wrap
|
|
||||||
import cats.Eval
|
import cats.Eval
|
||||||
import cats.data.Chain
|
import cats.data.Chain
|
||||||
import cats.free.Cofree
|
import cats.free.Cofree
|
||||||
@ -16,8 +15,10 @@ case class FuncOp(tree: Cofree[Chain, OpTag]) extends Model {
|
|||||||
Cofree.cata(tree)(folder)
|
Cofree.cata(tree)(folder)
|
||||||
|
|
||||||
def definesValueNames: Eval[Set[String]] = cata[Set[String]] {
|
def definesValueNames: Eval[Set[String]] = cata[Set[String]] {
|
||||||
case (CallArrowTag(_, _, Call(_, Some(export))), acc) => Eval.later(acc.foldLeft(Set(export))(_ ++ _))
|
case (CallArrowTag(_, _, Call(_, Some(export))), acc) =>
|
||||||
case (CallServiceTag(_, _, Call(_, Some(export)), _), acc) => Eval.later(acc.foldLeft(Set(export))(_ ++ _))
|
Eval.later(acc.foldLeft(Set(export))(_ ++ _))
|
||||||
|
case (CallServiceTag(_, _, Call(_, Some(export)), _), acc) =>
|
||||||
|
Eval.later(acc.foldLeft(Set(export))(_ ++ _))
|
||||||
case (NextTag(export), acc) => Eval.later(acc.foldLeft(Set(export))(_ ++ _))
|
case (NextTag(export), acc) => Eval.later(acc.foldLeft(Set(export))(_ ++ _))
|
||||||
case (_, acc) => Eval.later(acc.foldLeft(Set.empty[String])(_ ++ _))
|
case (_, acc) => Eval.later(acc.foldLeft(Set.empty[String])(_ ++ _))
|
||||||
}
|
}
|
||||||
@ -41,7 +42,7 @@ case class FuncOp(tree: Cofree[Chain, OpTag]) extends Model {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def resolveTopology(doVia: ValueModel => FuncOp): FuncOp =
|
def resolveTopology(): FuncOp =
|
||||||
FuncOp(FuncOp.transformWithPath(tree) {
|
FuncOp(FuncOp.transformWithPath(tree) {
|
||||||
case (path, c: CallServiceTag) =>
|
case (path, c: CallServiceTag) =>
|
||||||
Cofree[Chain, OpTag](
|
Cofree[Chain, OpTag](
|
||||||
@ -55,11 +56,12 @@ case class FuncOp(tree: Cofree[Chain, OpTag]) extends Model {
|
|||||||
OnTag(pid, Nil),
|
OnTag(pid, Nil),
|
||||||
Eval.now(
|
Eval.now(
|
||||||
Chain.fromSeq(
|
Chain.fromSeq(
|
||||||
via.map(doVia).map(_.tree)
|
via.map(FuncOp.noop).map(_.tree)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
case (_, t) => Cofree[Chain, OpTag](t, Eval.now(Chain.empty))
|
case (_, t) =>
|
||||||
|
Cofree[Chain, OpTag](t, Eval.now(Chain.empty))
|
||||||
})
|
})
|
||||||
|
|
||||||
def :+:(prev: FuncOp): FuncOp =
|
def :+:(prev: FuncOp): FuncOp =
|
||||||
@ -68,14 +70,21 @@ case class FuncOp(tree: Cofree[Chain, OpTag]) extends Model {
|
|||||||
|
|
||||||
object FuncOp {
|
object FuncOp {
|
||||||
|
|
||||||
|
def noop(peerId: ValueModel): FuncOp =
|
||||||
|
FuncOp.wrap(
|
||||||
|
OnTag(peerId, Nil),
|
||||||
|
FuncOp.leaf(CallServiceTag(LiteralModel("\"op\""), "identity", Call(Nil, None), Some(peerId)))
|
||||||
|
)
|
||||||
|
|
||||||
def traverseA[A](cf: Cofree[Chain, OpTag], init: A)(
|
def traverseA[A](cf: Cofree[Chain, OpTag], init: A)(
|
||||||
f: (A, OpTag) => (A, Cofree[Chain, OpTag])
|
f: (A, OpTag) => (A, Cofree[Chain, OpTag])
|
||||||
): Eval[(A, Cofree[Chain, OpTag])] = {
|
): Eval[(A, Cofree[Chain, OpTag])] = {
|
||||||
val (headA, head) = f(init, cf.head)
|
val (headA, head) = f(init, cf.head)
|
||||||
// TODO: it should be in standard library, with some other types
|
// TODO: it should be in standard library, with some other types
|
||||||
cf.tail
|
cf.tail
|
||||||
.map(_.foldLeft[(A, Chain[Cofree[Chain, OpTag]])]((headA, head.tailForced)) { case ((aggrA, aggrTail), child) =>
|
.map(_.foldLeft[(A, Chain[Cofree[Chain, OpTag]])]((headA, head.tailForced)) {
|
||||||
traverseA(child, aggrA)(f).value.map(aggrTail.append)
|
case ((aggrA, aggrTail), child) =>
|
||||||
|
traverseA(child, aggrA)(f).value.map(aggrTail.append)
|
||||||
})
|
})
|
||||||
.map(_.map(ch => head.copy(tail = Eval.now(ch))))
|
.map(_.map(ch => head.copy(tail = Eval.now(ch))))
|
||||||
}
|
}
|
||||||
@ -83,13 +92,13 @@ object FuncOp {
|
|||||||
def transformWithPath(cf: Cofree[Chain, OpTag], path: List[OpTag] = Nil)(
|
def transformWithPath(cf: Cofree[Chain, OpTag], path: List[OpTag] = Nil)(
|
||||||
f: (List[OpTag], OpTag) => Cofree[Chain, OpTag]
|
f: (List[OpTag], OpTag) => Cofree[Chain, OpTag]
|
||||||
): Cofree[Chain, OpTag] = {
|
): Cofree[Chain, OpTag] = {
|
||||||
val h = f(path, cf.head)
|
val newCf = f(path, cf.head)
|
||||||
Cofree[Chain, OpTag](
|
Cofree[Chain, OpTag](
|
||||||
h.head,
|
newCf.head,
|
||||||
(h.tail, cf.tail)
|
(newCf.tail, cf.tail)
|
||||||
.mapN(_ ++ _)
|
.mapN(_ ++ _)
|
||||||
// IF make foldLeft here, will be possible to get info from prev sibling
|
// IF make foldLeft here, will be possible to get info from prev sibling
|
||||||
.map(_.map(transformWithPath(_, h.head :: path)(f)))
|
.map(_.map(transformWithPath(_, newCf.head :: path)(f)))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,12 @@ import aqua.model.{FuncOp, Model}
|
|||||||
import aqua.parser.lexer.Token
|
import aqua.parser.lexer.Token
|
||||||
import aqua.parser.{Ast, Expr}
|
import aqua.parser.{Ast, Expr}
|
||||||
import aqua.semantics.rules.ReportError
|
import aqua.semantics.rules.ReportError
|
||||||
import aqua.semantics.rules.abilities.{AbilitiesAlgebra, AbilitiesInterpreter, AbilitiesState, AbilityOp}
|
import aqua.semantics.rules.abilities.{
|
||||||
|
AbilitiesAlgebra,
|
||||||
|
AbilitiesInterpreter,
|
||||||
|
AbilitiesState,
|
||||||
|
AbilityOp
|
||||||
|
}
|
||||||
import aqua.semantics.rules.names.{NameOp, NamesAlgebra, NamesInterpreter, NamesState}
|
import aqua.semantics.rules.names.{NameOp, NamesAlgebra, NamesInterpreter, NamesState}
|
||||||
import aqua.semantics.rules.types.{TypeOp, TypesAlgebra, TypesInterpreter, TypesState}
|
import aqua.semantics.rules.types.{TypeOp, TypesAlgebra, TypesInterpreter, TypesState}
|
||||||
import cats.Eval
|
import cats.Eval
|
||||||
@ -30,12 +35,13 @@ object Semantics {
|
|||||||
// TODO instead of foldRight, do slidingWindow for 2 elements, merge right associative ones
|
// TODO instead of foldRight, do slidingWindow for 2 elements, merge right associative ones
|
||||||
// Then foldLeft just like now
|
// Then foldLeft just like now
|
||||||
inners
|
inners
|
||||||
.foldRight[Free[G, List[Model]]](Free.pure[G, List[Model]](List.empty[Model])) { case (a, b) =>
|
.foldRight[Free[G, List[Model]]](Free.pure[G, List[Model]](List.empty[Model])) {
|
||||||
(a, b).mapN {
|
case (a, b) =>
|
||||||
case (prev: FuncOp, (next: FuncOp) :: tail) if next.isRightAssoc =>
|
(a, b).mapN {
|
||||||
(prev :+: next) :: tail
|
case (prev: FuncOp, (next: FuncOp) :: tail) if next.isRightAssoc =>
|
||||||
case (prev, acc) => prev :: acc
|
(prev :+: next) :: tail
|
||||||
}
|
case (prev, acc) => prev :: acc
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.map(_.reduceLeftOption(_ |+| _).getOrElse(Model.empty("AST is empty")))
|
.map(_.reduceLeftOption(_ |+| _).getOrElse(Model.empty("AST is empty")))
|
||||||
)
|
)
|
||||||
@ -58,13 +64,15 @@ object Semantics {
|
|||||||
import monocle.macros.syntax.all._
|
import monocle.macros.syntax.all._
|
||||||
|
|
||||||
implicit val re: ReportError[F, CompilerState[F]] =
|
implicit val re: ReportError[F, CompilerState[F]] =
|
||||||
(st: CompilerState[F], token: Token[F], hint: String) => st.focus(_.errors).modify(_.append(token -> hint))
|
(st: CompilerState[F], token: Token[F], hint: String) =>
|
||||||
|
st.focus(_.errors).modify(_.append(token -> hint))
|
||||||
|
|
||||||
implicit val ns: Lens[CompilerState[F], NamesState[F]] = GenLens[CompilerState[F]](_.names)
|
implicit val ns: Lens[CompilerState[F], NamesState[F]] = GenLens[CompilerState[F]](_.names)
|
||||||
|
|
||||||
val names = new NamesInterpreter[F, CompilerState[F]]()
|
val names = new NamesInterpreter[F, CompilerState[F]]()
|
||||||
|
|
||||||
implicit val as: Lens[CompilerState[F], AbilitiesState[F]] = GenLens[CompilerState[F]](_.abilities)
|
implicit val as: Lens[CompilerState[F], AbilitiesState[F]] =
|
||||||
|
GenLens[CompilerState[F]](_.abilities)
|
||||||
|
|
||||||
val abilities = new AbilitiesInterpreter[F, CompilerState[F]]()
|
val abilities = new AbilitiesInterpreter[F, CompilerState[F]]()
|
||||||
|
|
||||||
@ -78,11 +86,13 @@ object Semantics {
|
|||||||
free.foldMap[State[CompilerState[F], *]](interpreter)
|
free.foldMap[State[CompilerState[F], *]](interpreter)
|
||||||
}
|
}
|
||||||
|
|
||||||
def validate[F[_]](ast: Ast[F]): ValidatedNec[(Token[F], String), Model] =
|
def generateModel[F[_]](ast: Ast[F]): ValidatedNec[(Token[F], String), Model] =
|
||||||
(transpile[F] _ andThen interpret[F])(ast)
|
(transpile[F] _ andThen interpret[F])(ast)
|
||||||
.run(CompilerState[F]())
|
.run(CompilerState[F]())
|
||||||
.map { case (state, gen) =>
|
.map { case (state, gen) =>
|
||||||
NonEmptyChain.fromChain(state.errors).fold[ValidatedNec[(Token[F], String), Model]](Valid(gen))(Invalid(_))
|
NonEmptyChain
|
||||||
|
.fromChain(state.errors)
|
||||||
|
.fold[ValidatedNec[(Token[F], String), Model]](Valid(gen))(Invalid(_))
|
||||||
}
|
}
|
||||||
.value
|
.value
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import aqua.parser.expr.OnExpr
|
|||||||
import aqua.semantics.Prog
|
import aqua.semantics.Prog
|
||||||
import aqua.semantics.rules.ValuesAlgebra
|
import aqua.semantics.rules.ValuesAlgebra
|
||||||
import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
import aqua.semantics.rules.abilities.AbilitiesAlgebra
|
||||||
|
import cats.data.Chain
|
||||||
import cats.syntax.flatMap._
|
import cats.syntax.flatMap._
|
||||||
import cats.syntax.functor._
|
import cats.syntax.functor._
|
||||||
|
|
||||||
@ -24,7 +25,30 @@ class OnSem[F[_]](val expr: OnExpr[F]) extends AnyVal {
|
|||||||
(_: Unit, ops: Model) =>
|
(_: Unit, ops: Model) =>
|
||||||
A.endScope() as (ops match {
|
A.endScope() as (ops match {
|
||||||
case op: FuncOp =>
|
case op: FuncOp =>
|
||||||
FuncOp.wrap(OnTag(ValuesAlgebra.valueToModel(expr.peerId), expr.via.map(ValuesAlgebra.valueToModel)), op)
|
// 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(
|
||||||
|
OnTag(
|
||||||
|
ValuesAlgebra.valueToModel(expr.peerId),
|
||||||
|
path.map(ValuesAlgebra.valueToModel)
|
||||||
|
),
|
||||||
|
Chain(
|
||||||
|
op,
|
||||||
|
returnLeaf
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
case m => Model.error("On body is not an op, it's " + m)
|
case m => Model.error("On body is not an op, it's " + m)
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package aqua
|
package aqua
|
||||||
|
|
||||||
|
import aqua.model.Call
|
||||||
import aqua.parser.expr.{
|
import aqua.parser.expr.{
|
||||||
AbilityIdExpr,
|
AbilityIdExpr,
|
||||||
AliasExpr,
|
AliasExpr,
|
||||||
@ -37,17 +38,23 @@ import aqua.semantics.LiteralType.{bool, number, string}
|
|||||||
import aqua.semantics.{LiteralType, ScalarType}
|
import aqua.semantics.{LiteralType, ScalarType}
|
||||||
import org.scalatest.EitherValues
|
import org.scalatest.EitherValues
|
||||||
|
|
||||||
|
import scala.collection.mutable
|
||||||
import scala.language.implicitConversions
|
import scala.language.implicitConversions
|
||||||
|
|
||||||
object Utils {
|
object Utils {
|
||||||
implicit def toAb(str: String): Ability[Id] = Ability[Id](str)
|
implicit def toAb(str: String): Ability[Id] = Ability[Id](str)
|
||||||
|
|
||||||
implicit def toName(str: String): Name[Id] = Name[Id](str)
|
implicit def toName(str: String): Name[Id] = Name[Id](str)
|
||||||
|
implicit def toNameOp(str: Option[String]): Option[Name[Id]] = str.map(s => toName(s))
|
||||||
|
|
||||||
implicit def toFields(fields: List[String]): List[IntoField[Id]] = fields.map(f => IntoField[Id](f))
|
implicit def toFields(fields: List[String]): List[IntoField[Id]] =
|
||||||
|
fields.map(f => IntoField[Id](f))
|
||||||
|
|
||||||
implicit def toVar(name: String): VarLambda[Id] = VarLambda[Id](toName(name), Nil)
|
implicit def toVar(name: String): VarLambda[Id] = VarLambda[Id](toName(name), Nil)
|
||||||
|
|
||||||
|
implicit def toVarOp(name: Option[String]): Option[VarLambda[Id]] =
|
||||||
|
name.map(s => VarLambda[Id](toName(s), Nil))
|
||||||
|
|
||||||
implicit def toVarLambda(name: String, fields: List[String]): VarLambda[Id] =
|
implicit def toVarLambda(name: String, fields: List[String]): VarLambda[Id] =
|
||||||
VarLambda[Id](toName(name), toFields(fields))
|
VarLambda[Id](toName(name), toFields(fields))
|
||||||
implicit def toLiteral(name: String, t: LiteralType): Literal[Id] = Literal[Id](name, t)
|
implicit def toLiteral(name: String, t: LiteralType): Literal[Id] = Literal[Id](name, t)
|
||||||
@ -58,18 +65,30 @@ object Utils {
|
|||||||
implicit def toCustomType(str: String): CustomTypeToken[Id] = CustomTypeToken[Id](str)
|
implicit def toCustomType(str: String): CustomTypeToken[Id] = CustomTypeToken[Id](str)
|
||||||
def toArrayType(str: String): ArrayTypeToken[Id] = ArrayTypeToken[Id]((), str)
|
def toArrayType(str: String): ArrayTypeToken[Id] = ArrayTypeToken[Id]((), str)
|
||||||
|
|
||||||
implicit def toArrowType(args: List[DataTypeToken[Id]], res: Option[DataTypeToken[Id]]): ArrowTypeToken[Id] =
|
implicit def toArrowType(
|
||||||
|
args: List[DataTypeToken[Id]],
|
||||||
|
res: Option[DataTypeToken[Id]]
|
||||||
|
): ArrowTypeToken[Id] =
|
||||||
ArrowTypeToken[Id]((), args, res)
|
ArrowTypeToken[Id]((), args, res)
|
||||||
|
|
||||||
implicit def toCustomArg(str: String, customType: String): Arg[Id] = Arg[Id](str, toCustomType(customType))
|
implicit def toCustomArg(str: String, customType: String): Arg[Id] =
|
||||||
|
Arg[Id](str, toCustomType(customType))
|
||||||
|
|
||||||
implicit def toArg(str: String, typeToken: TypeToken[Id]): Arg[Id] = Arg[Id](str, typeToken)
|
implicit def toArg(str: String, typeToken: TypeToken[Id]): Arg[Id] = Arg[Id](str, typeToken)
|
||||||
|
|
||||||
|
implicit def toArgSc(str: String, scalarType: ScalarType): Arg[Id] =
|
||||||
|
Arg[Id](str, scToBt(scalarType))
|
||||||
|
|
||||||
implicit def scToBt(sc: ScalarType): BasicTypeToken[Id] = BasicTypeToken[Id](sc)
|
implicit def scToBt(sc: ScalarType): BasicTypeToken[Id] = BasicTypeToken[Id](sc)
|
||||||
|
|
||||||
|
val boolSc: BasicTypeToken[Id] = BasicTypeToken[Id](ScalarType.bool)
|
||||||
|
val stringSc: BasicTypeToken[Id] = BasicTypeToken[Id](ScalarType.string)
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Utils extends EitherValues {
|
trait Utils extends EitherValues {
|
||||||
|
|
||||||
|
val emptyCall = Call(Nil, None)
|
||||||
|
|
||||||
def parseExpr(str: String): CallArrowExpr[Id] =
|
def parseExpr(str: String): CallArrowExpr[Id] =
|
||||||
CallArrowExpr.p[Id].parseAll(str).value
|
CallArrowExpr.p[Id].parseAll(str).value
|
||||||
|
|
||||||
@ -110,4 +129,8 @@ trait Utils extends EitherValues {
|
|||||||
ArrowTypeExpr.p[Id].parseAll(str).value
|
ArrowTypeExpr.p[Id].parseAll(str).value
|
||||||
|
|
||||||
def funcExpr(str: String): FuncExpr[Id] = FuncExpr.p[Id].parseAll(str).value
|
def funcExpr(str: String): FuncExpr[Id] = FuncExpr.p[Id].parseAll(str).value
|
||||||
|
|
||||||
|
implicit class QueueHelper[T](q: mutable.Queue[T]) {
|
||||||
|
def d(): T = q.dequeue()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,44 @@
|
|||||||
package aqua.parser
|
package aqua.parser
|
||||||
|
|
||||||
import aqua.Utils
|
import aqua.model.{
|
||||||
import aqua.parser.Ast.Tree
|
Call,
|
||||||
import aqua.parser.expr.FuncExpr
|
CallArrowTag,
|
||||||
import aqua.parser.lexer.{ArrowTypeToken, BasicTypeToken}
|
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.parser.lexer.{ArrowTypeToken, BasicTypeToken, EqOp}
|
||||||
import aqua.semantics.ScalarType.{bool, u64}
|
import aqua.semantics.ScalarType.{bool, u64}
|
||||||
import cats.{Eval, Id}
|
import cats.{Eval, Id}
|
||||||
import cats.syntax.traverse._
|
import cats.syntax.traverse._
|
||||||
|
import cats.syntax.foldable._
|
||||||
import org.scalatest.flatspec.AnyFlatSpec
|
import org.scalatest.flatspec.AnyFlatSpec
|
||||||
import org.scalatest.matchers.should.Matchers
|
import org.scalatest.matchers.should.Matchers
|
||||||
import aqua.parser.lift.LiftParser.Implicits.idLiftParser
|
import aqua.parser.lift.LiftParser.Implicits.idLiftParser
|
||||||
|
import aqua.semantics.Semantics
|
||||||
import aqua.semantics.Semantics.{folder, Alg}
|
import aqua.semantics.Semantics.{folder, Alg}
|
||||||
import cats.data.Chain
|
import cats.data.{Chain, NonEmptyChain, Validated}
|
||||||
import cats.free.Cofree
|
import cats.free.Cofree
|
||||||
|
|
||||||
|
import scala.collection.mutable
|
||||||
import scala.language.implicitConversions
|
import scala.language.implicitConversions
|
||||||
|
|
||||||
class FuncExprSpec extends AnyFlatSpec with Matchers with Utils {
|
class FuncExprSpec extends AnyFlatSpec with Matchers with Utils {
|
||||||
@ -21,18 +46,35 @@ class FuncExprSpec extends AnyFlatSpec with Matchers with Utils {
|
|||||||
import aqua.semantics.ScalarType.{string, u32}
|
import aqua.semantics.ScalarType.{string, u32}
|
||||||
|
|
||||||
"func header" should "parse" in {
|
"func header" should "parse" in {
|
||||||
funcExpr("func some() -> bool") should be(FuncExpr("some", List(), Some(bool: BasicTypeToken[Id]), None))
|
funcExpr("func some() -> bool") should be(
|
||||||
|
FuncExpr("some", List(), Some(bool: BasicTypeToken[Id]), None)
|
||||||
|
)
|
||||||
funcExpr("func some()") should be(FuncExpr("some", List(), None, None))
|
funcExpr("func some()") should be(FuncExpr("some", List(), None, None))
|
||||||
|
|
||||||
val arrowToken = ArrowTypeToken[Id]((), List(BasicTypeToken[Id](u32)), Some(BasicTypeToken[Id](bool)))
|
val arrowToken =
|
||||||
|
ArrowTypeToken[Id]((), List(BasicTypeToken[Id](u32)), Some(BasicTypeToken[Id](bool)))
|
||||||
funcExpr("func some(peer: PeerId, other: u32 -> bool)") should be(
|
funcExpr("func some(peer: PeerId, other: u32 -> bool)") should be(
|
||||||
FuncExpr(toName("some"), List(toCustomArg("peer", "PeerId"), toArg("other", arrowToken)), None, None)
|
FuncExpr(
|
||||||
|
toName("some"),
|
||||||
|
List(toCustomArg("peer", "PeerId"), toArg("other", arrowToken)),
|
||||||
|
None,
|
||||||
|
None
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val arrowToken2 =
|
val arrowToken2 =
|
||||||
ArrowTypeToken[Id]((), List(BasicTypeToken[Id](u32), BasicTypeToken[Id](u64)), Some(BasicTypeToken[Id](bool)))
|
ArrowTypeToken[Id](
|
||||||
|
(),
|
||||||
|
List(BasicTypeToken[Id](u32), BasicTypeToken[Id](u64)),
|
||||||
|
Some(BasicTypeToken[Id](bool))
|
||||||
|
)
|
||||||
funcExpr("func some(peer: PeerId, other: u32, u64 -> bool)") should be(
|
funcExpr("func some(peer: PeerId, other: u32, u64 -> bool)") should be(
|
||||||
FuncExpr(toName("some"), List(toCustomArg("peer", "PeerId"), toArg("other", arrowToken2)), None, None)
|
FuncExpr(
|
||||||
|
toName("some"),
|
||||||
|
List(toCustomArg("peer", "PeerId"), toArg("other", arrowToken2)),
|
||||||
|
None,
|
||||||
|
None
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val arrowToken3 = ArrowTypeToken[Id]((), List(BasicTypeToken[Id](u32)), None)
|
val arrowToken3 = ArrowTypeToken[Id]((), List(BasicTypeToken[Id](u32)), None)
|
||||||
@ -44,25 +86,26 @@ class FuncExprSpec extends AnyFlatSpec with Matchers with Utils {
|
|||||||
None
|
None
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"on" should "parse on x: y" in {
|
def checkHeadGetTail(
|
||||||
val script =
|
tree: Cofree[Chain, Expr[Id]],
|
||||||
"""func a():
|
headCheck: Expr[Id],
|
||||||
| on peer.id:
|
lengthCheck: Int
|
||||||
| x <- Ab.func()
|
): Chain[Cofree[Chain, Expr[Id]]] = {
|
||||||
| Peer "some id"
|
tree.head should be(headCheck)
|
||||||
| call(true)""".stripMargin
|
val tail = tree.tailForced
|
||||||
|
tail.length should be(lengthCheck)
|
||||||
val c: Cofree[Chain, Expr[Id]] = FuncExpr.ast[Id](Indent()).parseAll(script).value
|
tail
|
||||||
val a = Ast(c).cata(folder[Id, Alg[Id, *]]).value
|
|
||||||
// a.run
|
|
||||||
println(a)
|
|
||||||
|
|
||||||
FuncExpr.ast[Id](Indent()).parseAll(script).isRight should be(true)
|
|
||||||
}
|
}
|
||||||
"if" should "parse if x == y" in {
|
|
||||||
|
def headTail[T](
|
||||||
|
tree: Cofree[Chain, T]
|
||||||
|
): (T, List[Cofree[Chain, T]]) = {
|
||||||
|
(tree.head, tree.tailForced.toList)
|
||||||
|
}
|
||||||
|
|
||||||
|
"func expr" should "parse on x: y" in {
|
||||||
val script =
|
val script =
|
||||||
"""func a():
|
"""func a():
|
||||||
| if peer.id == other:
|
| if peer.id == other:
|
||||||
@ -70,59 +113,153 @@ class FuncExprSpec extends AnyFlatSpec with Matchers with Utils {
|
|||||||
| Peer "some id"
|
| Peer "some id"
|
||||||
| call(true)""".stripMargin
|
| call(true)""".stripMargin
|
||||||
|
|
||||||
FuncExpr.ast[Id](Indent()).parseAll(script).isRight should be(true)
|
val tree = FuncExpr.ast[Id](Indent()).parseAll(script).value
|
||||||
|
val funcBody = checkHeadGetTail(tree, FuncExpr("a", Nil, None, None), 1).toList
|
||||||
|
println("body: " + funcBody)
|
||||||
|
|
||||||
|
val ifBody =
|
||||||
|
checkHeadGetTail(
|
||||||
|
funcBody.head,
|
||||||
|
IfExpr(toVarLambda("peer", List("id")), EqOp[Id](true), toVar("other")),
|
||||||
|
3
|
||||||
|
).toList
|
||||||
|
|
||||||
|
ifBody.head.head should be(CallArrowExpr(Some(toName("x")), Some(toAb("Ab")), "func", Nil))
|
||||||
|
ifBody(1).head should be(AbilityIdExpr(toAb("Peer"), toStr("some id")))
|
||||||
|
ifBody(2).head should be(CallArrowExpr(None, None, "call", List(toBool(true))))
|
||||||
|
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
|
|
||||||
"function" should "parse getTime as a whole" in {
|
"some" should "other" in {
|
||||||
val func =
|
val script =
|
||||||
"""func getTime(peer: PeerId, ret: u32 -> ()) -> string:
|
"""service Local("local"):
|
||||||
| on peer:
|
| gt: -> bool
|
||||||
| Peer "peer"
|
|
|
||||||
| t <- Peer.timestamp()
|
|func tryGen() -> bool:
|
||||||
| ret(t)""".stripMargin
|
| on "deeper" via "deep":
|
||||||
|
| v <- Local.gt()
|
||||||
|
| <- v
|
||||||
|
|
|
||||||
|
|func genC(val: string) -> bool:
|
||||||
|
| one <- Local.gt()
|
||||||
|
| on "smth" via "else":
|
||||||
|
| two <- tryGen()
|
||||||
|
| three <- Local.gt()
|
||||||
|
| <- two""".stripMargin
|
||||||
|
|
||||||
DefFunc.`deffunc`.parseAll(func).right.value should be(
|
val tree = parser[Id](Indent()).parseAll(script).value
|
||||||
DefFunc[Id, HNil](
|
|
||||||
getTimeHead,
|
val qTree = tree.tree.foldLeft(mutable.Queue.empty[Expr[Id]]) { case (acc, tag) =>
|
||||||
NonEmptyList.of(
|
acc.enqueue(tag)
|
||||||
On[Id, HNil](
|
|
||||||
VarLambda[Id]("peer", Nil),
|
|
||||||
NonEmptyList.of(
|
|
||||||
AbilityId[Id, HNil]("Peer", Literal[Id]("\"peer\"", LiteralType.string), HNil),
|
|
||||||
Extract[Id, HNil]("t", AbilityFuncCall[Id, HNil]("Peer", "timestamp", "Peer.timestamp", Nil, HNil), HNil)
|
|
||||||
),
|
|
||||||
HNil
|
|
||||||
),
|
|
||||||
FuncCall[Id, HNil]("ret", VarLambda[Id]("t", Nil) :: Nil, HNil)
|
|
||||||
),
|
|
||||||
HNil
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"function" should "parse getTime with no return" in {
|
qTree.d() shouldBe RootExpr()
|
||||||
val func =
|
// Local service
|
||||||
"""func getTime(peer: PeerId, ret: u32 -> ()) -> string:
|
qTree.d() shouldBe ServiceExpr(toAb("Local"), Some(toStr("local")))
|
||||||
| on peer:
|
qTree.d() shouldBe ArrowTypeExpr("gt", toArrowType(Nil, Some(scToBt(bool))))
|
||||||
| Peer "peer"
|
qTree.d() shouldBe FuncExpr("tryGen", Nil, Some(scToBt(bool)), Some("v"))
|
||||||
| t <- Peer.timestamp()""".stripMargin
|
qTree.d() shouldBe OnExpr(toStr("deeper"), List(toStr("deep")))
|
||||||
|
qTree.d() shouldBe CallArrowExpr(Some("v"), Some(toAb("Local")), "gt", Nil)
|
||||||
|
qTree.d() shouldBe ReturnExpr(toVar("v"))
|
||||||
|
// genC function
|
||||||
|
qTree.d() shouldBe FuncExpr("genC", List(toArgSc("val", string)), Some(boolSc), Some("two"))
|
||||||
|
qTree.d() shouldBe CallArrowExpr(Some("one"), Some(toAb("Local")), "gt", List())
|
||||||
|
qTree.d() shouldBe OnExpr(toStr("smth"), List(toStr("else")))
|
||||||
|
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"))
|
||||||
|
|
||||||
DefFunc.`deffunc`.parseAll(func).right.value should be(
|
val f =
|
||||||
DefFunc[Id, HNil](
|
Semantics.generateModel(tree).toList.head.asInstanceOf[ScriptModel]
|
||||||
getTimeHead,
|
|
||||||
NonEmptyList.of(
|
val funcs = f.funcs.toList
|
||||||
On[Id, HNil](
|
val funcTryGen = funcs.head
|
||||||
VarLambda[Id]("peer", Nil),
|
val funcOpTryGen = funcTryGen.body.resolveTopology()
|
||||||
NonEmptyList.of(
|
|
||||||
AbilityId[Id, HNil]("Peer", Literal[Id]("\"peer\"", LiteralType.string), HNil),
|
val qTryGen = funcOpTryGen.tree.foldLeft(mutable.Queue.empty[OpTag]) { case (acc, tag) =>
|
||||||
Extract[Id, HNil]("t", AbilityFuncCall[Id, HNil]("Peer", "timestamp", "Peer.timestamp", Nil, HNil), HNil)
|
acc.enqueue(tag)
|
||||||
),
|
}
|
||||||
HNil
|
|
||||||
)
|
val smth = LiteralModel("\"smth\"")
|
||||||
),
|
val smthOn = OnTag(smth, Nil)
|
||||||
HNil
|
val smthCall = CallServiceTag(
|
||||||
)
|
LiteralModel("\"op\""),
|
||||||
)
|
"identity",
|
||||||
}*/
|
emptyCall,
|
||||||
|
Some(LiteralModel("\"smth\""))
|
||||||
|
)
|
||||||
|
|
||||||
|
val elseL = LiteralModel("\"else\"")
|
||||||
|
val elseOn = OnTag(elseL, Nil)
|
||||||
|
val elseCall = CallServiceTag(
|
||||||
|
LiteralModel("\"op\""),
|
||||||
|
"identity",
|
||||||
|
emptyCall,
|
||||||
|
Some(LiteralModel("\"else\""))
|
||||||
|
)
|
||||||
|
|
||||||
|
val deeper = LiteralModel("\"deeper\"")
|
||||||
|
val deeperOn = OnTag(deeper, Nil)
|
||||||
|
val deeperCall = CallServiceTag(
|
||||||
|
LiteralModel("\"op\""),
|
||||||
|
"identity",
|
||||||
|
emptyCall,
|
||||||
|
Some(LiteralModel("\"deeper\""))
|
||||||
|
)
|
||||||
|
val deep = LiteralModel("\"deep\"")
|
||||||
|
val deepOn = OnTag(deep, Nil)
|
||||||
|
val deepCall = CallServiceTag(
|
||||||
|
LiteralModel("\"op\""),
|
||||||
|
"identity",
|
||||||
|
emptyCall,
|
||||||
|
Some(LiteralModel("\"deep\""))
|
||||||
|
)
|
||||||
|
val local = LiteralModel("\"local\"")
|
||||||
|
|
||||||
|
// tag that we will go to 'deeper'
|
||||||
|
qTryGen.d() shouldBe deeperOn
|
||||||
|
// move to 'deep' node
|
||||||
|
qTryGen.d() shouldBe deepOn
|
||||||
|
qTryGen.d() shouldBe deepCall
|
||||||
|
// move to 'deeper' node
|
||||||
|
qTryGen.d() shouldBe deeperOn
|
||||||
|
qTryGen.d() shouldBe deeperCall
|
||||||
|
// a call must be with `on` too, so we need to call 'deeper' after we will go out of the scope
|
||||||
|
qTryGen.d() shouldBe CallServiceTag(local, "gt", Call(Nil, Some("v")), Some(deeper))
|
||||||
|
qTryGen.d() shouldBe deepOn
|
||||||
|
// return to 'deeper' node
|
||||||
|
qTryGen.d() shouldBe deeperOn
|
||||||
|
qTryGen.d() shouldBe deeperCall
|
||||||
|
// return to 'deep' node
|
||||||
|
qTryGen.d() shouldBe deepOn
|
||||||
|
qTryGen.d() shouldBe deepCall
|
||||||
|
|
||||||
|
val funcGenComplex = f.funcs.toList(1)
|
||||||
|
val funcOpGenComplex = funcGenComplex.body.resolveTopology()
|
||||||
|
|
||||||
|
val qGenComplex = funcOpGenComplex.tree.foldLeft(mutable.Queue.empty[OpTag]) {
|
||||||
|
case (acc, tag) =>
|
||||||
|
acc.enqueue(tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
qGenComplex.d() shouldBe SeqTag
|
||||||
|
qGenComplex.d() shouldBe CallServiceTag(local, "gt", Call(List(), Some("one")), None)
|
||||||
|
// tag that we will go to 'smth'
|
||||||
|
qGenComplex.d() shouldBe smthOn
|
||||||
|
// move to 'else' node
|
||||||
|
qGenComplex.d() shouldBe elseOn
|
||||||
|
qGenComplex.d() shouldBe elseCall
|
||||||
|
// move to 'smth' node
|
||||||
|
qGenComplex.d() shouldBe smthOn
|
||||||
|
qGenComplex.d() shouldBe smthCall
|
||||||
|
qGenComplex.d() shouldBe CallArrowTag(None, "tryGen", Call(List(), Some("two")))
|
||||||
|
qGenComplex.d() shouldBe elseOn
|
||||||
|
// return to 'smth' node
|
||||||
|
qGenComplex.d() shouldBe smthOn
|
||||||
|
qGenComplex.d() shouldBe smthCall
|
||||||
|
// return to 'else' node
|
||||||
|
qGenComplex.d() shouldBe elseOn
|
||||||
|
qGenComplex.d() shouldBe elseCall
|
||||||
|
qGenComplex.d() shouldBe CallServiceTag(local, "gt", Call(List(), Some("three")), None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user