fix(compiler): Refactor values [fixes LNG-57] (#821)

* Refactor parser

* Savepoint

* Refactor CliFunc and TypesInterpreter

* Fix CliFunc

* Fix parser tests

* Fix tests compilation

* Fix callArrowToRaw

* Fix co parsing, add tests

* Add par tests

* Fix field parsing, add test

* Return ability to CallArrowToken

* Fix names

* Refactor and add comments

* Refactor, add comments

* Fix
This commit is contained in:
InversionSpaces 2023-08-15 14:33:34 +04:00 committed by GitHub
parent 6146f8e40a
commit f562bd40b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 888 additions and 577 deletions

View File

@ -4,68 +4,61 @@ import aqua.parser.lexer.{CallArrowToken, CollectionToken, LiteralToken, VarToke
import aqua.parser.lift.Span import aqua.parser.lift.Span
import aqua.raw.value.{CollectionRaw, LiteralRaw, ValueRaw, VarRaw} import aqua.raw.value.{CollectionRaw, LiteralRaw, ValueRaw, VarRaw}
import aqua.types.{ArrayType, BottomType} import aqua.types.{ArrayType, BottomType}
import cats.data.{NonEmptyList, Validated, ValidatedNel} import cats.data.{NonEmptyList, Validated, ValidatedNel}
import cats.data.Validated.{invalid, invalidNel, validNel} import cats.data.Validated.{invalid, invalidNel, validNel}
import cats.{Id, ~>} import cats.{~>, Id}
import cats.syntax.traverse.* import cats.syntax.traverse.*
import cats.syntax.validated.*
import cats.syntax.either.*
import cats.syntax.comonad.*
import cats.syntax.option.*
case class CliFunc(name: String, args: List[ValueRaw] = Nil, ability: Option[String] = None) case class CliFunc(name: String, args: List[ValueRaw] = Nil)
object CliFunc { object CliFunc {
def spanToId: Span.S ~> Id = new (Span.S ~> Id) { def spanToId: Span.S ~> Id = new (Span.S ~> Id) {
override def apply[A](span: Span.S[A]): Id[A] = { override def apply[A](span: Span.S[A]): Id[A] = span.extract
span._2
}
} }
def fromString(func: String): ValidatedNel[String, CliFunc] = { def fromString(func: String): ValidatedNel[String, CliFunc] = {
CallArrowToken.callArrow.parseAll(func.trim) match { CallArrowToken.callArrow
case Right(exprSpan) => .parseAll(func.trim)
val expr = exprSpan.mapK(spanToId) .toValidated
.leftMap(
val argsV = expr.args.collect { _.expected.map(_.context.mkString("\n"))
)
.map(_.mapK(spanToId))
.andThen(expr =>
expr.args.traverse {
case LiteralToken(value, ts) => case LiteralToken(value, ts) =>
validNel(LiteralRaw(value, ts)) LiteralRaw(value, ts).valid
case VarToken(name, _) => case VarToken(name) =>
validNel(VarRaw(name.value, BottomType)) VarRaw(name.value, BottomType).valid
case CollectionToken(_, values) => case CollectionToken(_, values) =>
val hasVariables = values.exists { values.traverse {
case LiteralToken(_, _) => false case LiteralToken(value, ts) =>
case _ => true LiteralRaw(value, ts).some
} case _ => none
if (!hasVariables) { }.toValid(
val literals = values.collect { case LiteralToken(value, ts) => "Array elements can only be numbers, strings, or booleans."
LiteralRaw(value, ts) ).ensure(
} "If the argument is an array, then it must contain elements of the same type."
val hasSameTypesOrEmpty = )(_.distinctBy(_.`type`).size <= 1)
literals.isEmpty || literals.map(_.baseType).toSet.size == 1 .map(
if (hasSameTypesOrEmpty) {
validNel(
NonEmptyList NonEmptyList
.fromList(literals) .fromList(_)
.map(l => CollectionRaw(l, ArrayType(l.head.baseType))) .map(l => CollectionRaw(l, ArrayType(l.head.baseType)))
.getOrElse(ValueRaw.Nil) .getOrElse(ValueRaw.Nil)
) )
} else .toValidatedNel
invalidNel(
"If the argument is an array, then it must contain elements of the same type."
)
} else
invalidNel(
"Array arguments can only have numbers, strings, or booleans."
)
case CallArrowToken(_, _, _) => case CallArrowToken(_, _, _) =>
invalidNel("Function calls as arguments are not supported.") "Function calls as arguments are not supported.".invalidNel
}.sequence case _ =>
argsV.andThen(args => "Unsupported argument.".invalidNel
validNel(CliFunc(expr.funcName.value, args, expr.ability.map(_.name))) }.map(args => CliFunc(expr.funcName.value, args))
) )
case Left(err) => invalid(err.expected.map(_.context.mkString("\n")))
}
} }
} }

View File

@ -9,6 +9,7 @@ import aqua.model.transform.TransformConfig
import aqua.model.{AquaContext, FuncArrow} import aqua.model.{AquaContext, FuncArrow}
import aqua.parser.lift.FileSpan import aqua.parser.lift.FileSpan
import aqua.run.CliFunc import aqua.run.CliFunc
import cats.data.Validated.{invalidNec, validNec} import cats.data.Validated.{invalidNec, validNec}
import cats.data.{Chain, NonEmptyList, Validated, ValidatedNec} import cats.data.{Chain, NonEmptyList, Validated, ValidatedNec}
import cats.effect.IO import cats.effect.IO
@ -19,6 +20,7 @@ import cats.syntax.functor.*
import cats.syntax.monad.* import cats.syntax.monad.*
import cats.syntax.show.* import cats.syntax.show.*
import cats.syntax.traverse.* import cats.syntax.traverse.*
import cats.syntax.option.*
import fs2.io.file.{Files, Path} import fs2.io.file.{Files, Path}
import scribe.Logging import scribe.Logging
@ -84,16 +86,9 @@ object FuncCompiler {
def findFunction( def findFunction(
contexts: Chain[AquaContext], contexts: Chain[AquaContext],
func: CliFunc func: CliFunc
): ValidatedNec[String, FuncArrow] = ): ValidatedNec[String, FuncArrow] = contexts
func.ability
.fold(
contexts
.collectFirstSome(_.allFuncs.get(func.name)) .collectFirstSome(_.allFuncs.get(func.name))
)(ab => contexts.collectFirstSome(_.abilities.get(ab).flatMap(_.allFuncs.get(func.name)))) .toValidNec(
.map(validNec) s"There is no function '${func.name}' or it is not exported. Check the spelling or see https://fluence.dev/docs/aqua-book/language/header/#export"
.getOrElse(
Validated.invalidNec[String, FuncArrow](
s"There is no function '${func.ability.map(_ + ".").getOrElse("")}${func.name}' or it is not exported. Check the spelling or see https://fluence.dev/docs/aqua-book/language/header/#export"
)
) )
} }

View File

@ -1,87 +1,7 @@
aqua Main service Srv("srv"):
call(x: i32) -> i32
use DECLARE_CONST, decl_bar from "declare.aqua" as Declare func main() -> i32:
arr = [1, 2, 3]
export SomeService, handleAb, bug214, checkAbCalls a <- Srv.call(0)
<- arr[Srv.call(1)]
service SomeService("wed"):
getStr(s: string) -> string
ability SomeAb:
someArrow(s: string) -> string, string
str: string
ability SecondAb:
arrow(s: string) -> string
num: u32
func funcStr(s: string) -> string, string:
strInFunc <- SomeService.getStr(Declare.DECLARE_CONST)
strInFunc2 <- SomeService.getStr(s)
<- strInFunc, strInFunc2
func handleSecAb {SomeAb, SecondAb}() -> string, string, string, u32:
SomeAb.someArrow("eferfrfrf")
b, c <- SomeAb.someArrow("efre")
d <- SecondAb.arrow(SomeAb.str)
<- b, c, d, SecondAb.num
func returnAb(s: string) -> SomeAb:
SomeAb = SomeAb(someArrow = funcStr, str = s)
<- SomeAb
func handleAb(fff: string) -> string, string, string, u32:
SomeAb = returnAb(fff)
SecondAb = SecondAb(arrow = funcStr, num = 12)
res1, res2, res3, res4 <- handleSecAb{SomeAb, SecondAb}()
<- res1, res2, res3, res4
data Struct:
int: i8
ability Simple:
st: Struct
arrow(x: i8) -> bool
ability Complex:
simple: Simple
field: string
func foo{Complex, Simple}() -> bool, bool:
closure = () -> bool:
<- Simple.st.int >= 0
res <- closure()
<- Complex.simple.arrow(
Complex.simple.st.int
), res
func bug214() -> bool, bool:
closure = (x: i8) -> bool:
<- x > 0
MyComplex = Complex(
simple = Simple(
st = Struct(int = 0),
arrow = closure
),
field = "complex"
)
res1, res2 <- foo{MyComplex, MyComplex.simple}()
<- res1, res2
ability SSS:
arrow(x: i8) -> bool
ability CCCC:
arrow(x: i8) -> bool
simple: SSS
func checkAbCalls() -> bool, bool:
closure = (x: i8) -> bool:
<- x > 20
MySSS = SSS(arrow = closure)
MyCCCC = CCCC(simple = MySSS, arrow = MySSS.arrow)
<- MySSS.arrow(42), MyCCCC.arrow(12)

View File

@ -19,7 +19,8 @@ case class IntoFieldRaw(name: String, `type`: Type) extends PropertyRaw {
override def varNames: Set[String] = Set.empty override def varNames: Set[String] = Set.empty
} }
case class IntoArrowRaw(name: String, arrowType: Type, arguments: List[ValueRaw]) extends PropertyRaw { case class IntoArrowRaw(name: String, arrowType: Type, arguments: List[ValueRaw])
extends PropertyRaw {
override def `type`: Type = arrowType override def `type`: Type = arrowType
@ -27,10 +28,12 @@ case class IntoArrowRaw(name: String, arrowType: Type, arguments: List[ValueRaw]
override def varNames: Set[String] = arguments.flatMap(_.varNames).toSet override def varNames: Set[String] = arguments.flatMap(_.varNames).toSet
override def renameVars(vals: Map[String, String]): PropertyRaw = copy(arguments = arguments.map(_.renameVars(vals))) override def renameVars(vals: Map[String, String]): PropertyRaw =
copy(arguments = arguments.map(_.renameVars(vals)))
} }
case class IntoCopyRaw(`type`: StructType, fields: NonEmptyMap[String, ValueRaw]) extends PropertyRaw { case class IntoCopyRaw(`type`: StructType, fields: NonEmptyMap[String, ValueRaw])
extends PropertyRaw {
override def map(f: ValueRaw => ValueRaw): IntoCopyRaw = copy(fields = fields.map(f)) override def map(f: ValueRaw => ValueRaw): IntoCopyRaw = copy(fields = fields.map(f))
override def varNames: Set[String] = Set.empty override def varNames: Set[String] = Set.empty
@ -38,14 +41,6 @@ case class IntoCopyRaw(`type`: StructType, fields: NonEmptyMap[String, ValueRaw]
override def renameVars(vals: Map[String, String]): IntoCopyRaw = this override def renameVars(vals: Map[String, String]): IntoCopyRaw = this
} }
case class MethodRaw(name: String, `type`: Type) extends PropertyRaw {
override def map(f: ValueRaw => ValueRaw): MethodRaw = this
override def renameVars(vals: Map[String, String]): MethodRaw = this
override def varNames: Set[String] = Set.empty
}
case class FunctorRaw(name: String, `type`: Type) extends PropertyRaw { case class FunctorRaw(name: String, `type`: Type) extends PropertyRaw {
override def map(f: ValueRaw => ValueRaw): FunctorRaw = this override def map(f: ValueRaw => ValueRaw): FunctorRaw = this

View File

@ -1,8 +1,10 @@
package aqua.raw.value package aqua.raw.value
import aqua.types.* import aqua.types.*
import cats.data.{Chain, NonEmptyList, NonEmptyMap} import cats.data.{Chain, NonEmptyList, NonEmptyMap}
import cats.Eq import cats.Eq
import cats.syntax.option.*
import scribe.Logging import scribe.Logging
sealed trait ValueRaw { sealed trait ValueRaw {
@ -263,3 +265,46 @@ case class CallArrowRaw(
s"(call ${ability.fold("")(a => s"|$a| ")} (${serviceId.fold("")(_.toString + " ")}$name) [${arguments s"(call ${ability.fold("")(a => s"|$a| ")} (${serviceId.fold("")(_.toString + " ")}$name) [${arguments
.mkString(" ")}] :: $baseType)" .mkString(" ")}] :: $baseType)"
} }
object CallArrowRaw {
def func(
funcName: String,
baseType: ArrowType,
arguments: List[ValueRaw] = Nil
): CallArrowRaw = CallArrowRaw(
ability = None,
name = funcName,
arguments = arguments,
baseType = baseType,
serviceId = None
)
def ability(
abilityName: String,
funcName: String,
baseType: ArrowType,
arguments: List[ValueRaw] = Nil
): CallArrowRaw = CallArrowRaw(
ability = None,
name = AbilityType.fullName(abilityName, funcName),
arguments = arguments,
baseType = baseType,
serviceId = None
)
def service(
abilityName: String,
serviceId: ValueRaw,
funcName: String,
baseType: ArrowType,
arguments: List[ValueRaw] = Nil
): CallArrowRaw = CallArrowRaw(
ability = abilityName.some,
name = funcName,
arguments = arguments,
baseType = baseType,
serviceId = Some(serviceId)
)
}

View File

@ -2,20 +2,15 @@ package aqua.parser.expr
import aqua.parser.Expr import aqua.parser.Expr
import aqua.parser.lexer.Token.* import aqua.parser.lexer.Token.*
import aqua.parser.lexer.{ import aqua.parser.lexer.*
CallArrowToken,
CollectionToken,
InfixToken,
LiteralToken,
Name,
ValueToken
}
import aqua.parser.lift.LiftParser import aqua.parser.lift.LiftParser
import cats.Comonad import cats.Comonad
import cats.parse.Parser as P import cats.parse.Parser as P
import cats.~> import cats.~>
import aqua.parser.lift.Span import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan} import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
import aqua.parser.lexer.PrefixToken
import aqua.parser.lexer.VarToken
case class ConstantExpr[F[_]]( case class ConstantExpr[F[_]](
name: Name[F], name: Name[F],
@ -35,20 +30,17 @@ object ConstantExpr extends Expr.Leaf {
override val p: P[ConstantExpr[Span.S]] = override val p: P[ConstantExpr[Span.S]] =
(((constName ~ `?`.?).with1 <* `=` <* ` `) ~ ValueToken.`value`).flatMap { (((constName ~ `?`.?).with1 <* `=` <* ` `) ~ ValueToken.`value`).flatMap {
case ((name, mark), value) => case ((name, mark), value) =>
lazy val fail = (what: String) =>
P.failWith(
s"'$name' is $what, but only strings, numbers or booleans can be used"
)
value match { value match {
case CollectionToken(point, _) => case CollectionToken(point, _) => fail("a collection")
P.failWith( case CallArrowToken(_, _, _) => fail("a function call")
s"'$name' is an array, but only strings, numbers or booleans can be used" case InfixToken(_, _, _) | PrefixToken(_, _) => fail("an expression")
) case PropertyToken(_, _) => fail("a property")
case CallArrowToken(_, _, _) => case NamedValueToken(_, _) => fail("an ability or data")
P.failWith( case LiteralToken(_, _) | VarToken(_) =>
s"'$name' is a function call, but only strings, numbers or booleans can be used"
)
case InfixToken(_, _, _) =>
P.failWith(
s"'$name' an expression, but only strings, numbers or booleans can be used"
)
case _ =>
P.pure(ConstantExpr(name, value, mark.nonEmpty)) P.pure(ConstantExpr(name, value, mark.nonEmpty))
} }

View File

@ -27,9 +27,8 @@ object ArrowExpr extends Expr.AndIndented {
// It is important for IfExpr to be before CallArrowExpr // It is important for IfExpr to be before CallArrowExpr
// because `if (1 + 1) == 2` is parsed as if `if(1 + 1)` is an arrow call // because `if (1 + 1) == 2` is parsed as if `if(1 + 1)` is an arrow call
IfExpr :: IfExpr ::
CallArrowExpr ::
TryExpr ::
ElseOtherwiseExpr :: ElseOtherwiseExpr ::
TryExpr ::
CatchExpr :: CatchExpr ::
Expr.defer(ParExpr) :: Expr.defer(ParExpr) ::
Expr.defer(CoExpr) :: Expr.defer(CoExpr) ::
@ -37,6 +36,10 @@ object ArrowExpr extends Expr.AndIndented {
DeclareStreamExpr :: DeclareStreamExpr ::
Expr.defer(ClosureExpr) :: Expr.defer(ClosureExpr) ::
AssignmentExpr :: AssignmentExpr ::
// It is important for CallArrowExpr to be last
// because it can parse prefixes of other expressions
// e.g. `if` could be parsed as variable name
CallArrowExpr ::
Nil Nil
override val validChildren: List[Expr.Lexem] = override val validChildren: List[Expr.Lexem] =

View File

@ -20,7 +20,7 @@ case class AssignmentExpr[F[_]](
object AssignmentExpr extends Expr.Leaf { object AssignmentExpr extends Expr.Leaf {
override val p: P[AssignmentExpr[Span.S]] = override val p: P[AssignmentExpr[Span.S]] =
(((Name.cl | Name.p) <* ` = `).with1 ~ ValueToken.`value`).flatMap { case (variable, value) => ((Name.variable <* ` = `).with1 ~ ValueToken.`value`).flatMap { case (variable, value) =>
value match { value match {
case CollectionToken(_, values) => case CollectionToken(_, values) =>
if (values.isEmpty) if (values.isEmpty)

View File

@ -12,7 +12,9 @@ import cats.{~>, Comonad}
case class CallArrowExpr[F[_]]( case class CallArrowExpr[F[_]](
variables: List[Name[F]], variables: List[Name[F]],
callArrow: CallArrowToken[F] // Here `ValueToken` is used to allow
// a, b <- ServiceOrAbility.call()
callArrow: ValueToken[F]
) extends Expr[F](CallArrowExpr, callArrow) { ) extends Expr[F](CallArrowExpr, callArrow) {
def mapK[K[_]: Comonad](fk: F ~> K): CallArrowExpr[K] = def mapK[K[_]: Comonad](fk: F ~> K): CallArrowExpr[K] =
@ -27,9 +29,9 @@ object CallArrowExpr extends Expr.Leaf {
override val p: P[CallArrowExpr[Span.S]] = { override val p: P[CallArrowExpr[Span.S]] = {
val variables: P0[Option[NonEmptyList[Name[Span.S]]]] = (comma(Name.p) <* ` <- `).backtrack.? val variables: P0[Option[NonEmptyList[Name[Span.S]]]] = (comma(Name.p) <* ` <- `).backtrack.?
(variables.with1 ~ CallArrowToken.callArrow.withContext( // TODO: Restrict to function call only
"Only results of a function call can be written to a stream" // or allow any expression?
)).map { case (variables, token) => (variables.with1 ~ ValueToken.value).map { case (variables, token) =>
CallArrowExpr(variables.toList.flatMap(_.toList), token) CallArrowExpr(variables.toList.flatMap(_.toList), token)
} }
} }

View File

@ -3,14 +3,14 @@ package aqua.parser.expr.func
import aqua.parser.Expr import aqua.parser.Expr
import aqua.parser.expr.* import aqua.parser.expr.*
import aqua.parser.lexer.Token.* import aqua.parser.lexer.Token.*
import aqua.parser.lexer.{ValueToken, VarToken} import aqua.parser.lexer.{PropertyToken, ValueToken}
import aqua.parser.lift.{LiftParser, Span} import aqua.parser.lift.{LiftParser, Span}
import aqua.parser.lift.Span.{P0ToSpan, PToSpan} import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
import cats.parse.Parser import cats.parse.Parser
import cats.{~>, Comonad} import cats.{~>, Comonad}
import cats.data.NonEmptyList import cats.data.NonEmptyList
case class JoinExpr[F[_]](values: NonEmptyList[VarToken[F]]) case class JoinExpr[F[_]](values: NonEmptyList[ValueToken[F]])
extends Expr[F](JoinExpr, values.head) { extends Expr[F](JoinExpr, values.head) {
override def mapK[K[_]: Comonad](fk: F ~> K): JoinExpr[K] = override def mapK[K[_]: Comonad](fk: F ~> K): JoinExpr[K] =
@ -20,5 +20,5 @@ case class JoinExpr[F[_]](values: NonEmptyList[VarToken[F]])
object JoinExpr extends Expr.Leaf { object JoinExpr extends Expr.Leaf {
override val p: Parser[JoinExpr[Span.S]] = override val p: Parser[JoinExpr[Span.S]] =
(`join` *> ` ` *> comma(ValueToken.varProperty)).map(JoinExpr(_)) (`join` *> ` ` *> comma(PropertyToken.property)).map(JoinExpr(_))
} }

View File

@ -20,7 +20,9 @@ case class ParExpr[F[_]](point: Token[F]) extends Expr[F](ParExpr, point) {
object ParExpr extends Expr.Prefix() { object ParExpr extends Expr.Prefix() {
override def continueWith: List[Expr.Lexem] = override def continueWith: List[Expr.Lexem] =
CallArrowExpr :: OnExpr :: ForExpr :: JoinExpr :: Nil // Here it is important for CallArrowExpr to be last
// because it could parse prefixes of other expressions
OnExpr :: ForExpr :: JoinExpr :: CallArrowExpr :: Nil
override val p: Parser[Expr[Span.S]] = override val p: Parser[Expr[Span.S]] =
`par`.lift.map(Token.lift[Span.S, Unit](_)).map(ParExpr(_)) `par`.lift.map(Token.lift[Span.S, Unit](_)).map(ParExpr(_))

View File

@ -14,6 +14,8 @@ import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
case class Name[F[_]: Comonad](name: F[String]) extends Token[F] { case class Name[F[_]: Comonad](name: F[String]) extends Token[F] {
override def as[T](v: T): F[T] = name.as(v) override def as[T](v: T): F[T] = name.as(v)
def asTypeToken: NamedTypeToken[F] = NamedTypeToken(name)
override def mapK[K[_]: Comonad](fk: F ~> K): Name[K] = copy(fk(name)) override def mapK[K[_]: Comonad](fk: F ~> K): Name[K] = copy(fk(name))
def rename(newName: String): Name[F] = copy(name.as(newName)) def rename(newName: String): Name[F] = copy(name.as(newName))
@ -30,16 +32,12 @@ object Name {
val p: P[Name[Span.S]] = val p: P[Name[Span.S]] =
`name`.lift.map(Name(_)) `name`.lift.map(Name(_))
val cl: P[Name[Span.S]] = val variable: P[Name[Span.S]] =
`Class`.lift.map(Name(_)) (name | Class).lift.map(Name(_))
val upper: P[Name[Span.S]] = val upper: P[Name[Span.S]] =
NAME.lift.map(Name(_)) NAME.lift.map(Name(_))
val dotted: P[Name[Span.S]] =
((`Class` ~ `.`).backtrack.rep0.?.with1 ~ P.oneOf(`name` :: NAME :: Nil)).string.lift
.map(Name(_))
val nameAs: P[As[Span.S]] = val nameAs: P[As[Span.S]] =
asOpt(p) asOpt(p)
} }

View File

@ -14,15 +14,18 @@ import cats.~>
import aqua.parser.lift.Span import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan} import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
import aqua.types.LiteralType import aqua.types.LiteralType
import aqua.parser.lexer.CallArrowToken.CallBraces
sealed trait PropertyOp[F[_]] extends Token[F] { sealed trait PropertyOp[F[_]] extends Token[F] {
def mapK[K[_]: Comonad](fk: F ~> K): PropertyOp[K] def mapK[K[_]: Comonad](fk: F ~> K): PropertyOp[K]
} }
case class IntoArrow[F[_]: Comonad](name: Name[F], arguments: List[ValueToken[F]]) extends PropertyOp[F] { case class IntoArrow[F[_]: Comonad](name: Name[F], arguments: List[ValueToken[F]])
extends PropertyOp[F] {
override def as[T](v: T): F[T] = name.as(v) override def as[T](v: T): F[T] = name.as(v)
override def mapK[K[_]: Comonad](fk: F ~> K): PropertyOp[K] = copy(name.mapK(fk), arguments.map(_.mapK(fk))) override def mapK[K[_]: Comonad](fk: F ~> K): PropertyOp[K] =
copy(name.mapK(fk), arguments.map(_.mapK(fk)))
override def toString: String = s".$name(${arguments.map(_.toString).mkString(", ")})" override def toString: String = s".$name(${arguments.map(_.toString).mkString(", ")})"
} }
@ -55,10 +58,12 @@ case class IntoCopy[F[_]: Comonad](point: F[Unit], fields: NonEmptyMap[String, V
object PropertyOp { object PropertyOp {
private val parseField: P[PropertyOp[Span.S]] = private val parseField: P[PropertyOp[Span.S]] =
(`.` *> `name`).lift.map(IntoField(_)) (`.` *> anyName).lift.map(IntoField(_))
val parseArrow: P[PropertyOp[Span.S]] = val parseArrow: P[PropertyOp[Span.S]] =
(`.` *> CallArrowToken.callBraces()).lift.map(p => IntoArrow(p._2._1, p._2._2 ++ p._2._3)) (`.` *> CallArrowToken.callBraces).map { case CallBraces(name, abilities, args) =>
IntoArrow(name, abilities ++ args)
}
val parseCopy: P[PropertyOp[Span.S]] = val parseCopy: P[PropertyOp[Span.S]] =
(`.` *> (`copy`.lift ~ namedArgs)).map { case (point, fields) => (`.` *> (`copy`.lift ~ namedArgs)).map { case (point, fields) =>
@ -67,7 +72,10 @@ object PropertyOp {
private val parseIdx: P[PropertyOp[Span.S]] = private val parseIdx: P[PropertyOp[Span.S]] =
(P.defer( (P.defer(
(ValueToken.`value`.surroundedBy(`/s*`).between(`[`.between(` *`, `/s*`), `/s*` *> `]`).lift | (exclamation *> ValueToken.num).lift) (ValueToken.`value`
.surroundedBy(`/s*`)
.between(`[`.between(` *`, `/s*`), `/s*` *> `]`)
.lift | (exclamation *> ValueToken.num).lift)
.map(v => IntoIndex(v.map(_.unit), Some(v._2))) .map(v => IntoIndex(v.map(_.unit), Some(v._2)))
.backtrack .backtrack
) | exclamation.lift.map(e => IntoIndex(e, None))).flatMap { ii => ) | exclamation.lift.map(e => IntoIndex(e, None))).flatMap { ii =>

View File

@ -28,7 +28,9 @@ object Token {
private val inAZ = P.charIn(AZ) private val inAZ = P.charIn(AZ)
private val inaz = P.charIn(az) private val inaz = P.charIn(az)
private val whileAnum = P.charsWhile(anum_) private val inaZ = P.charIn(az ++ AZ)
private val whileAnum_ = P.charsWhile(anum_)
private val whileUpperAnum_ = P.charsWhile(upperAnum_)
val ` *` : P0[String] = P.charsWhile0(fSpaces) val ` *` : P0[String] = P.charsWhile0(fSpaces)
val ` ` : P[String] = P.charsWhile(fSpaces) val ` ` : P[String] = P.charsWhile(fSpaces)
@ -68,14 +70,13 @@ object Token {
val `copy`: P[Unit] = P.string("copy") val `copy`: P[Unit] = P.string("copy")
val `:` : P[Unit] = P.char(':') val `:` : P[Unit] = P.char(':')
val ` : ` : P[Unit] = P.char(':').surroundedBy(` `.?) val ` : ` : P[Unit] = P.char(':').surroundedBy(` `.?)
val `anum_*` : P[Unit] = whileAnum.void val `anum_*` : P[Unit] = whileAnum_.void
val NAME: P[String] = (inAZ ~ P.charsWhile(upperAnum_).?).string val NAME: P[String] = (inAZ ~ whileUpperAnum_.?).string
val `name`: P[String] = (inaz ~ whileAnum.?).string val `name`: P[String] = (inaz ~ whileAnum_.?).string
val `Class`: P[String] = (inAZ ~ whileAnum_.?).string
val anyName: P[String] = (inaZ ~ whileAnum_.?).string
val `Class`: P[String] = (inAZ ~ whileAnum.backtrack.?).map { case (c, s)
c.toString ++ s.getOrElse("")
}
val `\n` : P[Unit] = P.string("\n\r") | P.char('\n') | P.string("\r\n") val `\n` : P[Unit] = P.string("\n\r") | P.char('\n') | P.string("\r\n")
val `--` : P[Unit] = ` `.?.with1 *> P.string("--") <* ` `.? val `--` : P[Unit] = ` `.?.with1 *> P.string("--") <* ` `.?
@ -119,17 +120,16 @@ object Token {
val `/s*` : P0[Unit] = ` \n+`.backtrack | ` *`.void val `/s*` : P0[Unit] = ` \n+`.backtrack | ` *`.void
val namedArg: P[(String, ValueToken[S])] = val namedArg: P[(String, ValueToken[S])] =
P.defer(`name`.between(` *`, `/s*`) ~ P.defer(
`name`.between(` *`, `/s*`) ~
`=`.between(` *`, `/s*`).void ~ `=`.between(` *`, `/s*`).void ~
ValueToken.`value`.between(` *`, `/s*`)).map { case ((name, _), vt) => ValueToken.`value`.between(` *`, `/s*`)
).map { case ((name, _), vt) =>
(name, vt) (name, vt)
} }
val namedArgs: P[NonEmptyList[(String, ValueToken[S])]] = P.defer( val namedArgs: P[NonEmptyList[(String, ValueToken[S])]] =
((` `.?.with1 *> P.char('(') <* `/s*`) ~ comma( P.defer(` `.?.with1 ~ `(` ~ `/s*` *> comma(namedArg) <* `/s*` *> `)`)
namedArg
) <* (`/s*` *> P.char(')'))).map(_._2)
)
case class LiftToken[F[_]: Functor, A](point: F[A]) extends Token[F] { case class LiftToken[F[_]: Functor, A](point: F[A]) extends Token[F] {
override def as[T](v: T): F[T] = Functor[F].as(point, v) override def as[T](v: T): F[T] = Functor[F].as(point, v)

View File

@ -16,16 +16,153 @@ import cats.{~>, Comonad, Functor}
import cats.data.{NonEmptyList, NonEmptyMap} import cats.data.{NonEmptyList, NonEmptyMap}
import cats.syntax.foldable.* import cats.syntax.foldable.*
import cats.arrow.FunctionK import cats.arrow.FunctionK
import cats.syntax.traverse.*
import cats.syntax.option.*
sealed trait ValueToken[F[_]] extends Token[F] { sealed trait ValueToken[F[_]] extends Token[F] {
def mapK[K[_]: Comonad](fk: F ~> K): ValueToken[K] def mapK[K[_]: Comonad](fk: F ~> K): ValueToken[K]
} }
case class VarToken[F[_]](name: Name[F], property: List[PropertyOp[F]] = Nil) case class PropertyToken[F[_]: Comonad](
extends ValueToken[F] { value: ValueToken[F],
properties: NonEmptyList[PropertyOp[F]]
) extends ValueToken[F] {
override def as[T](v: T): F[T] = value.as(v)
def mapK[K[_]: Comonad](fk: F ~> K): PropertyToken[K] =
copy(value.mapK(fk), properties.map(_.mapK(fk)))
private def isClass(name: String): Boolean =
name.headOption.exists(_.isUpper)
private def isField(name: String): Boolean =
name.headOption.exists(_.isLower)
private def isConst(name: String): Boolean =
name.forall(c => !c.isLetter || c.isUpper)
/**
* This method tries to convert property token to
* call arrow token.
*
* Next properties pattern is transformed:
* (Class)+ arrow()
* ^^^^^^^
* this part is transformed to ability name.
*/
private def toCallArrow: Option[CallArrowToken[F]] = value match {
case VarToken(name) =>
val ability = properties.init.traverse {
case f @ IntoField(_) => f.value.some
case _ => none
}.map(
name.value +: _
).filter(
_.forall(isClass)
).map(props => name.rename(props.mkString(".")))
(properties.last, ability) match {
case (IntoArrow(funcName, args), Some(ability)) =>
CallArrowToken(
ability.asTypeToken.some,
funcName,
args
).some
case _ => none
}
case _ => none
}
/**
* This method tries to convert property token to
* property token with dotted var name inside value token.
*
* Next properties pattern is untouched:
* Class (field)*
*
* Next properties pattern is transformed:
* (Class)* (CONST | field) ..props..
* ^^^^^^^^^^^^^^^^^^^^^^^^
* this part is transformed to dotted name.
*/
private def toDottedName: Option[ValueToken[F]] = value match {
case VarToken(name) =>
// Pattern `Class (field)*` is ability access
// and should not be transformed
val isAbility = isClass(name.value) && properties.forall {
case f @ IntoField(_) => isField(f.value)
case _ => true
}
if (isAbility) none
else {
// Gather prefix of properties that are IntoField
val props = name.value +: properties.toList.view.map {
case IntoField(name) => name.extract.some
case _ => none
}.takeWhile(_.isDefined).flatten.toList
val propsWithIndex = props.zipWithIndex
// Find first property that is not Class
val classesTill = propsWithIndex.find { case (name, _) =>
!isClass(name)
}.collect { case (_, idx) =>
idx
}.getOrElse(props.length)
// Find last property after classes
// that is CONST or field
val lastSuitable = propsWithIndex
.take(classesTill)
.findLast { case (name, _) =>
isConst(name) || isField(name)
}
.collect { case (_, idx) => idx }
lastSuitable.map(last =>
val newProps = NonEmptyList.fromList(
properties.toList.drop(last + 1)
)
val newName = props.take(last + 1).mkString(".")
val varToken = VarToken(name.rename(newName))
newProps.fold(varToken)(props => PropertyToken(varToken, props))
)
}
case _ => none
}
/**
* This is a hacky method to adjust parsing result
* to format that was used previously.
* This method tries to convert property token to
* call arrow token or property token with
* dotted var name inside value token.
*
* @return Some(token) if token was adjusted, None otherwise
*/
def adjust: Option[ValueToken[F]] =
toCallArrow.orElse(toDottedName)
}
object PropertyToken {
val property: P[ValueToken[Span.S]] =
(ValueToken.basic ~ PropertyOp.ops.backtrack.?).map { case (v, ops) =>
ops.fold(v)(ops => PropertyToken(v, ops))
}
}
case class VarToken[F[_]](name: Name[F]) extends ValueToken[F] {
override def as[T](v: T): F[T] = name.as(v) override def as[T](v: T): F[T] = name.as(v)
def mapK[K[_]: Comonad](fk: F ~> K): VarToken[K] = copy(name.mapK(fk), property.map(_.mapK(fk))) def mapK[K[_]: Comonad](fk: F ~> K): VarToken[K] = copy(name.mapK(fk))
}
object VarToken {
lazy val variable: P[VarToken[Span.S]] = Name.variable.map(VarToken(_))
} }
case class LiteralToken[F[_]: Comonad](valueToken: F[String], ts: LiteralType) case class LiteralToken[F[_]: Comonad](valueToken: F[String], ts: LiteralType)
@ -74,6 +211,9 @@ object CollectionToken {
} }
case class CallArrowToken[F[_]: Comonad]( case class CallArrowToken[F[_]: Comonad](
// NOTE: Call with ability is not parsed by CallArrowToken
// it is parsed by PropertyToken and then adjusted
// It is done for legacy support reasons
ability: Option[NamedTypeToken[F]], ability: Option[NamedTypeToken[F]],
funcName: Name[F], funcName: Name[F],
args: List[ValueToken[F]] args: List[ValueToken[F]]
@ -87,17 +227,23 @@ case class CallArrowToken[F[_]: Comonad](
object CallArrowToken { object CallArrowToken {
def apply[F[_]: Comonad](funcName: Name[F], args: List[ValueToken[F]]): CallArrowToken[F] =
CallArrowToken(None, funcName, args)
case class CallBraces(name: Name[S], abilities: List[ValueToken[S]], args: List[ValueToken[S]]) case class CallBraces(name: Name[S], abilities: List[ValueToken[S]], args: List[ValueToken[S]])
// {SomeAb, SecondAb} for ValueToken // {SomeAb, SecondAb} for ValueToken
def abilities(): P[NonEmptyList[ValueToken[S]]] = def abilities(): P[NonEmptyList[ValueToken[S]]] =
`{` *> comma(ValueToken.`value`.surroundedBy(`/s*`)) <* `}` `{` *> comma(ValueToken.`value`.surroundedBy(`/s*`)) <* `}`
def callBraces(): P[CallBraces] = P lazy val callBraces: P[CallBraces] = P
.defer( .defer(
Name.p Name.p ~
~ abilities().? ~ comma0(ValueToken.`value`.surroundedBy(`/s*`)) abilities().? ~
.between(` `.?.with1 *> `(` <* `/s*`, `/s*` *> `)`) comma0(ValueToken.`value`.surroundedBy(`/s*`)).between(
` `.?.with1 *> `(` <* `/s*`,
`/s*` *> `)`
)
) )
.map { case ((n, ab), args) => .map { case ((n, ab), args) =>
CallBraces(n, ab.map(_.toList).getOrElse(Nil), args) CallBraces(n, ab.map(_.toList).getOrElse(Nil), args)
@ -107,12 +253,8 @@ object CallArrowToken {
) )
val callArrow: P[CallArrowToken[Span.S]] = val callArrow: P[CallArrowToken[Span.S]] =
((NamedTypeToken.dotted <* `.`).?.with1 ~ callBraces.map { braces =>
callBraces() CallArrowToken(braces.name, braces.abilities ++ braces.args)
.withContext(
"Missing braces '()' after the function call"
)).map { case (ab, callBraces) =>
CallArrowToken(ab, callBraces.name, callBraces.abilities ++ callBraces.args)
} }
} }
@ -135,7 +277,7 @@ object NamedValueToken {
"Missing braces '()' after the struct type" "Missing braces '()' after the struct type"
) )
.map { case (dn, args) => .map { case (dn, args) =>
NamedValueToken(NamedTypeToken(dn), NonEmptyMap.of(args.head, args.tail: _*)) NamedValueToken(NamedTypeToken(dn), args.toNem)
} }
} }
@ -409,16 +551,6 @@ object PrefixToken {
object ValueToken { object ValueToken {
val varProperty: P[VarToken[Span.S]] =
(Name.dotted ~ PropertyOp.ops.?).map { case (n, l)
VarToken(n, l.foldMap(_.toList))
}
val abProperty: P[VarToken[Span.S]] =
(Name.cl ~ PropertyOp.ops.?).map { case (n, l)
VarToken(n, l.foldMap(_.toList))
}
val bool: P[LiteralToken[Span.S]] = val bool: P[LiteralToken[Span.S]] =
P.oneOf( P.oneOf(
("true" :: "false" :: Nil) ("true" :: "false" :: Nil)
@ -456,20 +588,23 @@ object ValueToken {
private def brackets(basic: P[ValueToken[Span.S]]): P[ValueToken[Span.S]] = private def brackets(basic: P[ValueToken[Span.S]]): P[ValueToken[Span.S]] =
basic.between(`(`, `)`).backtrack basic.between(`(`, `)`).backtrack
// Basic element of math expression // Basic element of value expression
val atom: P[ValueToken[S]] = P.oneOf( // (without property access)
val basic = P.oneOf(
literal.backtrack :: literal.backtrack ::
initPeerId.backtrack :: initPeerId.backtrack ::
P.defer(CollectionToken.collection).backtrack :: P.defer(CollectionToken.collection).backtrack ::
P.defer(NamedValueToken.dataValue).backtrack :: P.defer(NamedValueToken.dataValue).backtrack ::
P.defer(CallArrowToken.callArrow).backtrack :: P.defer(CallArrowToken.callArrow).backtrack ::
P.defer(abProperty).backtrack :: P.defer(VarToken.variable).backtrack ::
P.defer(PrefixToken.value).backtrack :: P.defer(PrefixToken.value).backtrack ::
P.defer(brackets(InfixToken.value)).backtrack :: P.defer(brackets(value)).backtrack ::
varProperty ::
Nil Nil
) )
// Atomic element of math expression
val atom: P[ValueToken[S]] = P.defer(PropertyToken.property)
// One of entry points for parsing the whole math expression // One of entry points for parsing the whole math expression
val `value`: P[ValueToken[Span.S]] = val `value`: P[ValueToken[Span.S]] =
P.defer(InfixToken.value) P.defer(InfixToken.value)

View File

@ -20,6 +20,7 @@ import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan} import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
import cats.~> import cats.~>
import cats.syntax.bifunctor.* import cats.syntax.bifunctor.*
import cats.data.NonEmptyList
import scala.collection.mutable import scala.collection.mutable
import scala.language.implicitConversions import scala.language.implicitConversions
@ -33,35 +34,43 @@ object AquaSpec {
} }
} }
implicit def toAb(str: String): Ability[Id] = Ability[Id](str) def toName(str: String): Name[Id] = Name[Id](str)
implicit def toName(str: String): Name[Id] = Name[Id](str) def toNameOp(str: Option[String]): Option[Name[Id]] = str.map(s => toName(s))
implicit def toNameOp(str: Option[String]): Option[Name[Id]] = str.map(s => toName(s))
implicit def toFields(fields: List[String]): List[IntoField[Id]] = def toAb(str: String): Ability[Id] = Ability[Id](str)
fields.map(f => IntoField[Id](f))
implicit def toVar(name: String): VarToken[Id] = VarToken[Id](toName(name), Nil) def toVar(name: String): VarToken[Id] = VarToken[Id](toName(name))
implicit def toVarOp(name: Option[String]): Option[VarToken[Id]] = def toVarOp(name: Option[String]): Option[VarToken[Id]] =
name.map(s => VarToken[Id](toName(s), Nil)) name.map(toVar)
implicit def toVarLambda(name: String, fields: List[String]): VarToken[Id] = def toVarLambda(name: String, fields: List[String]): ValueToken[Id] =
VarToken[Id](toName(name), toFields(fields)) NonEmptyList
.fromList(fields)
.fold(toVar(name))(fs =>
PropertyToken(
toVar(name),
fs.map(IntoField[Id].apply)
)
)
implicit def toVarIndex(name: String, idx: Int): VarToken[Id] = def toVarIndex(name: String, idx: Int): PropertyToken[Id] =
VarToken[Id](toName(name), IntoIndex[Id](toNumber(idx).unit, Some(toNumber(idx))) :: Nil) PropertyToken[Id](
implicit def toLiteral(name: String, t: LiteralType): LiteralToken[Id] = LiteralToken[Id](name, t) VarToken[Id](toName(name)),
NonEmptyList.one(IntoIndex[Id](toNumber(idx).unit, Some(toNumber(idx))))
)
implicit def toNumber(n: Int): LiteralToken[Id] = def toLiteral(name: String, t: LiteralType): LiteralToken[Id] = LiteralToken[Id](name, t)
LiteralToken[Id](n.toString, LiteralType.forInt(n))
implicit def toBool(n: Boolean): LiteralToken[Id] = LiteralToken[Id](n.toString, bool)
implicit def toStr(n: String): LiteralToken[Id] = LiteralToken[Id]("\"" + n + "\"", string)
implicit def toNamedType(str: String): NamedTypeToken[Id] = NamedTypeToken[Id](str) def toNumber(n: Int): LiteralToken[Id] = LiteralToken[Id](n.toString, LiteralType.forInt(n))
def toBool(n: Boolean): LiteralToken[Id] = LiteralToken[Id](n.toString, bool)
def toStr(n: String): LiteralToken[Id] = LiteralToken[Id]("\"" + n + "\"", string)
def toNamedType(str: String): NamedTypeToken[Id] = NamedTypeToken[Id](str)
def toArrayType(str: String): ArrayTypeToken[Id] = ArrayTypeToken[Id]((), str) def toArrayType(str: String): ArrayTypeToken[Id] = ArrayTypeToken[Id]((), str)
implicit def toArrowType( def toArrowType(
args: List[DataTypeToken[Id]], args: List[DataTypeToken[Id]],
res: Option[DataTypeToken[Id]] res: Option[DataTypeToken[Id]]
): ArrowTypeToken[Id] = ): ArrowTypeToken[Id] =
@ -73,18 +82,23 @@ object AquaSpec {
): ArrowTypeToken[Id] = ): ArrowTypeToken[Id] =
ArrowTypeToken[Id]((), args.map(ab => Some(Name[Id](ab._1)) -> ab._2), res) ArrowTypeToken[Id]((), args.map(ab => Some(Name[Id](ab._1)) -> ab._2), res)
implicit def toNamedArg(str: String, customType: String): Arg[Id] = def toNamedArg(str: String, customType: String): Arg[Id] =
Arg[Id](str, toNamedType(customType)) Arg[Id](str, toNamedType(customType))
implicit def toArg(str: String, typeToken: TypeToken[Id]): Arg[Id] = Arg[Id](str, typeToken) def toArg(str: String, typeToken: TypeToken[Id]): Arg[Id] = Arg[Id](str, typeToken)
implicit def toArgSc(str: String, scalarType: ScalarType): Arg[Id] = def toArgSc(str: String, scalarType: ScalarType): Arg[Id] =
Arg[Id](str, scToBt(scalarType)) Arg[Id](str, scToBt(scalarType))
implicit def scToBt(sc: ScalarType): BasicTypeToken[Id] = BasicTypeToken[Id](sc) def scToBt(sc: ScalarType): BasicTypeToken[Id] = BasicTypeToken[Id](sc)
val boolSc: BasicTypeToken[Id] = BasicTypeToken[Id](ScalarType.bool) val boolSc: BasicTypeToken[Id] = BasicTypeToken[Id](ScalarType.bool)
val stringSc: BasicTypeToken[Id] = BasicTypeToken[Id](ScalarType.string) val stringSc: BasicTypeToken[Id] = BasicTypeToken[Id](ScalarType.string)
given Conversion[String, Name[Id]] = toName
given Conversion[String, NamedTypeToken[Id]] = toNamedType
given Conversion[Int, LiteralToken[Id]] = toNumber
given Conversion[ScalarType, BasicTypeToken[Id]] = scToBt
} }
trait AquaSpec extends EitherValues { trait AquaSpec extends EitherValues {
@ -116,8 +130,8 @@ trait AquaSpec extends EitherValues {
def parseAssign(str: String): AssignmentExpr[Id] = def parseAssign(str: String): AssignmentExpr[Id] =
AssignmentExpr.p.parseAll(str).value.mapK(spanToId) AssignmentExpr.p.parseAll(str).value.mapK(spanToId)
def parseVar(str: String): VarToken[Id] = def parseVar(str: String): ValueToken[Id] =
ValueToken.varProperty.parseAll(str).value.mapK(spanToId) ValueToken.value.parseAll(str).value.mapK(spanToId)
def parseData(str: String): NamedValueToken[Id] = def parseData(str: String): NamedValueToken[Id] =
NamedValueToken.dataValue.parseAll(str).value.mapK(spanToId) NamedValueToken.dataValue.parseAll(str).value.mapK(spanToId)

View File

@ -16,14 +16,15 @@ class AbilityValueExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
import AquaSpec.* import AquaSpec.*
private def parseAndCheckAbility(str: String) = { private def parseAndCheckAbility(str: String) = {
parseData( parseData(str) should be(
str
) should be(
NamedValueToken( NamedValueToken(
NamedTypeToken[Id]("AbilityA"), NamedTypeToken[Id]("AbilityA"),
NonEmptyMap.of( NonEmptyMap.of(
"v1" -> toNumber(1), "v1" -> toNumber(1),
"f1" -> VarToken(Name[Id]("input"), IntoField[Id]("arrow") :: Nil) "f1" -> PropertyToken[Id](
VarToken(toName("input")),
NonEmptyList.one(IntoField("arrow"))
)
) )
) )
) )

View File

@ -9,7 +9,7 @@ import org.scalatest.matchers.should.Matchers
class AliasExprSpec extends AnyFlatSpec with Matchers with AquaSpec { class AliasExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
import AquaSpec._ import AquaSpec.{given, *}
"alias" should "be parsed properly" in { "alias" should "be parsed properly" in {
parseAlias("alias SomeAlias : u32") should be( parseAlias("alias SomeAlias : u32") should be(

View File

@ -10,7 +10,7 @@ import org.scalatest.matchers.should.Matchers
class ArrowTypeExprSpec extends AnyFlatSpec with Matchers with AquaSpec { class ArrowTypeExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
import AquaSpec._ import AquaSpec.{given, *}
"arrow types" should "be parsed properly" in { "arrow types" should "be parsed properly" in {
parseArrow("onIn: string -> ()") should be( parseArrow("onIn: string -> ()") should be(
@ -31,7 +31,14 @@ class ArrowTypeExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
parseArrow("onIn{SomeAb}(a: Custom, b: Custom2)") should be( parseArrow("onIn{SomeAb}(a: Custom, b: Custom2)") should be(
ArrowTypeExpr[Id]( ArrowTypeExpr[Id](
"onIn", "onIn",
toNamedArrow(List("SomeAb" -> toNamedType("SomeAb"), "a" -> toNamedType("Custom"), "b" -> toNamedType("Custom2")), Nil) toNamedArrow(
List(
"SomeAb" -> toNamedType("SomeAb"),
"a" -> toNamedType("Custom"),
"b" -> toNamedType("Custom2")
),
Nil
)
) )
) )

View File

@ -8,7 +8,7 @@ import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
class AssignmentExprSpec extends AnyFlatSpec with Matchers with AquaSpec { class AssignmentExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
import AquaSpec._ import AquaSpec.{given, *}
"assign" should "be parsed" in { "assign" should "be parsed" in {
parseAssign("a = \"b\"") should be( parseAssign("a = \"b\"") should be(

View File

@ -2,7 +2,9 @@ package aqua.parser
import aqua.AquaSpec import aqua.AquaSpec
import aqua.parser.expr.func.CallArrowExpr import aqua.parser.expr.func.CallArrowExpr
import aqua.parser.lexer.{CallArrowToken, Name, VarToken} import aqua.parser.lexer.{CallArrowToken, IntoArrow, Name, PropertyToken, VarToken}
import cats.data.NonEmptyList
import cats.Id import cats.Id
import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
@ -12,19 +14,25 @@ class CallArrowSpec extends AnyFlatSpec with Matchers with AquaSpec {
"func calls" should "parse func()" in { "func calls" should "parse func()" in {
parseExpr("func()") should be( parseExpr("func()") should be(
CallArrowExpr[Id](Nil, CallArrowToken(None, toName("func"), Nil)) CallArrowExpr[Id](Nil, CallArrowToken(toName("func"), Nil))
) )
parseExpr("Ab.func(arg)") should be( parseExpr("Ab.func(arg)") should be(
CallArrowExpr[Id]( CallArrowExpr[Id](
Nil, Nil,
CallArrowToken(Some(toNamedType("Ab")), Name[Id]("func"), List(VarToken[Id](toName("arg")))) PropertyToken[Id](
VarToken[Id](toName("Ab")),
NonEmptyList.one(
IntoArrow(toName("func"), toVar("arg") :: Nil)
)
)
) )
) )
parseExpr("func(arg.doSomething)") should be( parseExpr("func(arg.doSomething)") should be(
CallArrowExpr[Id]( CallArrowExpr[Id](
Nil, Nil,
CallArrowToken(None, Name[Id]("func"), List(toVarLambda("arg", List("doSomething")))) CallArrowToken(Name[Id]("func"), List(toVarLambda("arg", List("doSomething"))))
) )
) )
@ -32,7 +40,6 @@ class CallArrowSpec extends AnyFlatSpec with Matchers with AquaSpec {
CallArrowExpr[Id]( CallArrowExpr[Id](
Nil, Nil,
CallArrowToken( CallArrowToken(
None,
Name[Id]("func"), Name[Id]("func"),
List(toVarLambda("arg", List("doSomething", "and", "doSomethingElse"))) List(toVarLambda("arg", List("doSomething", "and", "doSomethingElse")))
) )
@ -43,7 +50,6 @@ class CallArrowSpec extends AnyFlatSpec with Matchers with AquaSpec {
CallArrowExpr[Id]( CallArrowExpr[Id](
Nil, Nil,
CallArrowToken( CallArrowToken(
None,
Name[Id]("func"), Name[Id]("func"),
List(toVarLambda("arg", List("doSomething", "and", "doSomethingElse"))) List(toVarLambda("arg", List("doSomething", "and", "doSomethingElse")))
) )
@ -53,9 +59,11 @@ class CallArrowSpec extends AnyFlatSpec with Matchers with AquaSpec {
parseExpr("Ab.func(arg.doSomething.and.doSomethingElse, arg2.someFunc)") should be( parseExpr("Ab.func(arg.doSomething.and.doSomethingElse, arg2.someFunc)") should be(
CallArrowExpr[Id]( CallArrowExpr[Id](
Nil, Nil,
CallArrowToken( PropertyToken[Id](
Some(toNamedType("Ab")), VarToken[Id](toName("Ab")),
Name[Id]("func"), NonEmptyList.one(
IntoArrow(
toName("func"),
List( List(
toVarLambda("arg", List("doSomething", "and", "doSomethingElse")), toVarLambda("arg", List("doSomething", "and", "doSomethingElse")),
toVarLambda("arg2", List("someFunc")) toVarLambda("arg2", List("someFunc"))
@ -63,12 +71,13 @@ class CallArrowSpec extends AnyFlatSpec with Matchers with AquaSpec {
) )
) )
) )
)
)
parseExpr("x <- func(arg.doSomething)") should be( parseExpr("x <- func(arg.doSomething)") should be(
CallArrowExpr[Id]( CallArrowExpr[Id](
List(toName("x")), List(toName("x")),
CallArrowToken( CallArrowToken(
None,
Name[Id]("func"), Name[Id]("func"),
List( List(
toVarLambda("arg", List("doSomething")) toVarLambda("arg", List("doSomething"))
@ -81,7 +90,6 @@ class CallArrowSpec extends AnyFlatSpec with Matchers with AquaSpec {
CallArrowExpr[Id]( CallArrowExpr[Id](
toName("x") :: toName("y") :: toName("z") :: Nil, toName("x") :: toName("y") :: toName("z") :: Nil,
CallArrowToken( CallArrowToken(
None,
Name[Id]("func"), Name[Id]("func"),
List( List(
toVarLambda("arg", List("doSomething")) toVarLambda("arg", List("doSomething"))

View File

@ -2,8 +2,16 @@ package aqua.parser
import aqua.AquaSpec import aqua.AquaSpec
import aqua.parser.expr.{FuncExpr, RootExpr} import aqua.parser.expr.{FuncExpr, RootExpr}
import aqua.parser.expr.func.{ArrowExpr, CallArrowExpr, ClosureExpr, ReturnExpr} import aqua.parser.expr.func.{ArrowExpr, AssignmentExpr, CallArrowExpr, ClosureExpr, ReturnExpr}
import aqua.parser.lexer.{Ability, CallArrowToken, NamedTypeToken, Token, VarToken} import aqua.parser.lexer.{
Ability,
CallArrowToken,
IntoArrow,
NamedTypeToken,
PropertyToken,
Token,
VarToken
}
import aqua.types.ScalarType.string import aqua.types.ScalarType.string
import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
@ -15,7 +23,7 @@ import scala.collection.mutable
class ClosureExprSpec extends AnyFlatSpec with Matchers with AquaSpec { class ClosureExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
import AquaSpec._ import AquaSpec.{given, *}
val parser = Parser.spanParser val parser = Parser.spanParser
@ -53,16 +61,26 @@ class ClosureExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
qTree.d() shouldBe ArrowExpr(toNamedArrow(("s", scToBt(string)) :: Nil, scToBt(string) :: Nil)) qTree.d() shouldBe ArrowExpr(toNamedArrow(("s", scToBt(string)) :: Nil, scToBt(string) :: Nil))
qTree.d() shouldBe CallArrowExpr( qTree.d() shouldBe CallArrowExpr(
Nil, Nil,
CallArrowToken(Some(NamedTypeToken[Id]("LocalSrv")), toName("inside"), Nil) PropertyToken[Id](
VarToken[Id](toName("LocalSrv")),
NonEmptyList.one(
IntoArrow[Id](toName("inside"), Nil)
)
)
) )
qTree.d() shouldBe CallArrowExpr( qTree.d() shouldBe CallArrowExpr(
toName("p2Id") :: Nil, toName("p2Id") :: Nil,
CallArrowToken(Some(NamedTypeToken[Id]("Peer")), toName("identify"), Nil) PropertyToken[Id](
VarToken[Id](toName("Peer")),
NonEmptyList.one(
IntoArrow[Id](toName("identify"), Nil)
)
)
) )
qTree.d() shouldBe ReturnExpr(NonEmptyList(VarToken[Id](toName("p2Id")), Nil)) qTree.d() shouldBe ReturnExpr(NonEmptyList(VarToken[Id](toName("p2Id")), Nil))
qTree.d() shouldBe CallArrowExpr( qTree.d() shouldBe CallArrowExpr(
toName("v") :: Nil, toName("v") :: Nil,
CallArrowToken(None, toName("closure"), toStr("input") :: Nil) CallArrowToken(toName("closure"), toStr("input") :: Nil)
) )
qTree.d() shouldBe ReturnExpr(NonEmptyList(VarToken[Id](toName("v")), Nil)) qTree.d() shouldBe ReturnExpr(NonEmptyList(VarToken[Id](toName("v")), Nil))
} }

View File

@ -1,34 +1,95 @@
package aqua.parser package aqua.parser
import aqua.AquaSpec import aqua.AquaSpec
import aqua.AquaSpec.spanToId import aqua.AquaSpec.*
import aqua.parser.expr.func.{CallArrowExpr, CoExpr} import aqua.parser.expr.func.{CallArrowExpr, CoExpr, ForExpr, JoinExpr, OnExpr}
import aqua.parser.lexer.{CallArrowToken, Token} import aqua.parser.lexer.{CallArrowToken, Token}
import aqua.parser.lift.LiftParser.Implicits.idLiftParser import aqua.parser.lift.LiftParser.Implicits.idLiftParser
import cats.data.Chain
import cats.data.{Chain, NonEmptyList}
import cats.free.Cofree import cats.free.Cofree
import cats.{Eval, Id} import cats.{Eval, Id}
import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
import org.scalatest.Inside
class CoExprSpec extends AnyFlatSpec with Matchers with AquaSpec { class CoExprSpec extends AnyFlatSpec with Matchers with Inside with AquaSpec {
"co" should "be parsed" in { def insideCo(str: String)(testFun: Ast.Tree[Id] => Any) =
CoExpr.readLine.parseAll("co x <- y()").value.map(_.mapK(spanToId)).forceAll should be( inside(CoExpr.readLine.parseAll(str).map(_.map(_.mapK(spanToId)).forceAll)) {
Cofree[Chain, Expr[Id]]( case Right(tree) => testFun(tree)
CoExpr[Id](Token.lift[Id, Unit](())), }
def co(expr: Expr[Id]): Ast.Tree[Id] =
Cofree(
CoExpr(Token.lift(())),
Eval.now( Eval.now(
Chain( Chain(
Cofree[Chain, Expr[Id]]( Cofree(
CallArrowExpr( expr,
List(AquaSpec.toName("x")),
CallArrowToken(None, AquaSpec.toName("y"), Nil)
),
Eval.now(Chain.empty) Eval.now(Chain.empty)
) )
) )
) )
) )
"co" should "be parsed" in {
insideCo("co x <- y()")(
_ should be(
co(
CallArrowExpr(
List(toName("x")),
CallArrowToken(toName("y"), Nil)
)
)
)
)
insideCo("co call()")(
_ should be(
co(
CallArrowExpr(
Nil,
CallArrowToken(toName("call"), Nil)
)
)
)
)
insideCo("co on call() via relay:")(
_ should be(
co(
OnExpr(
CallArrowToken(toName("call"), Nil),
toVar("relay") :: Nil
)
)
)
)
insideCo("co join call(), x")(
_ should be(
co(
JoinExpr(
NonEmptyList.of(
CallArrowToken(toName("call"), Nil),
toVar("x")
)
)
)
)
)
insideCo("co for w <- getWorkers():")(
_ should be(
co(
ForExpr(
toName("w"),
CallArrowToken(toName("getWorkers"), Nil),
None
)
)
)
) )
} }
} }

View File

@ -9,7 +9,7 @@ import org.scalatest.matchers.should.Matchers
class DataStructExprSpec extends AnyFlatSpec with Matchers with AquaSpec { class DataStructExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
import AquaSpec._ import AquaSpec.{given, *}
"data struct" should "be parsed properly" in { "data struct" should "be parsed properly" in {
parseDataStruct("data Smth") should be( parseDataStruct("data Smth") should be(

View File

@ -8,7 +8,7 @@ import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
class FieldTypeExprSpec extends AnyFlatSpec with Matchers with AquaSpec { class FieldTypeExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
import AquaSpec._ import AquaSpec.{given, *}
"else" should "be parsed" in { "else" should "be parsed" in {
parseFieldType("some: bool") should be( parseFieldType("some: bool") should be(

View File

@ -7,7 +7,7 @@ import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
class ForExprSpec extends AnyFlatSpec with Matchers with AquaSpec { class ForExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
import AquaSpec._ import AquaSpec.{given, *}
"for expression" should "be parsed" in { "for expression" should "be parsed" in {
parseFor("for some <- \"a\"") should be( parseFor("for some <- \"a\"") should be(

View File

@ -29,9 +29,12 @@ import scala.collection.mutable
import scala.language.implicitConversions import scala.language.implicitConversions
import aqua.parser.lift.Span import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan} import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
import aqua.parser.lexer.PropertyToken
import aqua.parser.lexer.IntoArrow
import aqua.parser.expr.func.AssignmentExpr
class FuncExprSpec extends AnyFlatSpec with Matchers with Inside with Inspectors with AquaSpec { class FuncExprSpec extends AnyFlatSpec with Matchers with Inside with Inspectors with AquaSpec {
import AquaSpec._ import AquaSpec.{given, *}
private val parser = Parser.spanParser private val parser = Parser.spanParser
@ -110,11 +113,19 @@ class FuncExprSpec extends AnyFlatSpec with Matchers with Inside with Inspectors
).toList ).toList
ifBody.head.head.mapK(spanToId) should be( ifBody.head.head.mapK(spanToId) should be(
CallArrowExpr(List(toName("x")), CallArrowToken(Some(toNamedType("Ab")), "func", Nil)) CallArrowExpr(
List(toName("x")),
PropertyToken[Id](
VarToken[Id](toName("Ab")),
NonEmptyList.one(
IntoArrow[Id](toName("func"), Nil)
)
)
)
) )
ifBody(1).head.mapK(spanToId) should be(AbilityIdExpr(toNamedType("Peer"), toStr("some id"))) ifBody(1).head.mapK(spanToId) should be(AbilityIdExpr(toNamedType("Peer"), toStr("some id")))
ifBody(2).head.mapK(spanToId) should be( ifBody(2).head.mapK(spanToId) should be(
CallArrowExpr(Nil, CallArrowToken(None, "call", List(toBool(true)))) CallArrowExpr(Nil, CallArrowToken("call", List(toBool(true))))
) )
} }
@ -255,7 +266,12 @@ class FuncExprSpec extends AnyFlatSpec with Matchers with Inside with Inspectors
qTree.d() shouldBe OnExpr(toStr("deeper"), List(toStr("deep"))) qTree.d() shouldBe OnExpr(toStr("deeper"), List(toStr("deep")))
qTree.d() shouldBe CallArrowExpr( qTree.d() shouldBe CallArrowExpr(
List("v"), List("v"),
CallArrowToken(Some(toNamedType("Local")), "gt", Nil) PropertyToken[Id](
VarToken[Id](toName("Local")),
NonEmptyList.one(
IntoArrow[Id](toName("gt"), Nil)
)
)
) )
qTree.d() shouldBe ReturnExpr(NonEmptyList.one(toVar("v"))) qTree.d() shouldBe ReturnExpr(NonEmptyList.one(toVar("v")))
// genC function // genC function
@ -267,13 +283,23 @@ class FuncExprSpec extends AnyFlatSpec with Matchers with Inside with Inspectors
qTree.d() shouldBe ArrowExpr(toNamedArrow(("val" -> string) :: Nil, boolSc :: Nil)) qTree.d() shouldBe ArrowExpr(toNamedArrow(("val" -> string) :: Nil, boolSc :: Nil))
qTree.d() shouldBe CallArrowExpr( qTree.d() shouldBe CallArrowExpr(
List("one"), List("one"),
CallArrowToken(Some(toNamedType("Local")), "gt", Nil) PropertyToken[Id](
VarToken[Id](toName("Local")),
NonEmptyList.one(
IntoArrow[Id](toName("gt"), Nil)
)
)
) )
qTree.d() shouldBe OnExpr(toStr("smth"), List(toStr("else"))) qTree.d() shouldBe OnExpr(toStr("smth"), List(toStr("else")))
qTree.d() shouldBe CallArrowExpr(List("two"), CallArrowToken(None, "tryGen", Nil)) qTree.d() shouldBe CallArrowExpr(List("two"), CallArrowToken("tryGen", Nil))
qTree.d() shouldBe CallArrowExpr( qTree.d() shouldBe CallArrowExpr(
List("three"), List("three"),
CallArrowToken(Some(toNamedType("Local")), "gt", Nil) PropertyToken[Id](
VarToken[Id](toName("Local")),
NonEmptyList.one(
IntoArrow[Id](toName("gt"), Nil)
)
)
) )
qTree.d() shouldBe ReturnExpr(NonEmptyList.one(toVar("two"))) qTree.d() shouldBe ReturnExpr(NonEmptyList.one(toVar("two")))
} }

View File

@ -3,9 +3,19 @@ package aqua.parser
import aqua.AquaSpec import aqua.AquaSpec
import aqua.parser.expr.func.IfExpr import aqua.parser.expr.func.IfExpr
import aqua.parser.lexer.InfixToken.Op.{Add, Sub} import aqua.parser.lexer.InfixToken.Op.{Add, Sub}
import aqua.parser.lexer.{CallArrowToken, CollectionToken, InfixToken} import aqua.parser.lexer.{
CallArrowToken,
CollectionToken,
InfixToken,
IntoArrow,
PropertyToken,
ValueToken,
VarToken
}
import aqua.parser.lexer.CollectionToken.Mode.OptionMode import aqua.parser.lexer.CollectionToken.Mode.OptionMode
import cats.Id import cats.Id
import cats.data.NonEmptyList
import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
@ -41,27 +51,43 @@ class IfExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
parseIf("if Op.identity(\"str\") == \"a\"") should be( parseIf("if Op.identity(\"str\") == \"a\"") should be(
IfExpr[Id]( IfExpr[Id](
equ( equ(
CallArrowToken[Id](Some(toNamedType("Op")), toName("identity"), toStr("str") :: Nil), PropertyToken[Id](
VarToken[Id](toName("Op")),
NonEmptyList.one(
IntoArrow(toName("identity"), toStr("str") :: Nil)
)
),
toStr("a") toStr("a")
) )
) )
) )
parseIf("if Op.identity(\"str\") != Op.identity(\"str\")") should be( parseIf("if Op.identity(\"str\") != Op.identity(\"str\")") should be {
val operand = PropertyToken[Id](
VarToken[Id](toName("Op")),
NonEmptyList.one(
IntoArrow(toName("identity"), toStr("str") :: Nil)
)
)
IfExpr[Id]( IfExpr[Id](
neq( neq(
CallArrowToken[Id](Some(toNamedType("Op")), toName("identity"), toStr("str") :: Nil), operand,
CallArrowToken[Id](Some(toNamedType("Op")), toName("identity"), toStr("str") :: Nil) operand
)
) )
) )
}
parseIf("if 2 - 3 != Op.identity(4) + 5") should be( parseIf("if 2 - 3 != Op.identity(4) + 5") should be(
IfExpr[Id]( IfExpr[Id](
neq( neq(
sub(toNumber(2), toNumber(3)), sub(toNumber(2), toNumber(3)),
add( add(
CallArrowToken[Id](Some(toNamedType("Op")), toName("identity"), toNumber(4) :: Nil), PropertyToken[Id](
VarToken[Id](toName("Op")),
NonEmptyList.one(
IntoArrow(toName("identity"), toNumber(4) :: Nil)
)
),
toNumber(5) toNumber(5)
) )
) )
@ -71,8 +97,8 @@ class IfExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
parseIf("if funcCall(3) == funcCall2(4)") should be( parseIf("if funcCall(3) == funcCall2(4)") should be(
IfExpr[Id]( IfExpr[Id](
equ( equ(
CallArrowToken[Id](None, toName("funcCall"), toNumber(3) :: Nil), CallArrowToken[Id](toName("funcCall"), toNumber(3) :: Nil),
CallArrowToken[Id](None, toName("funcCall2"), toNumber(4) :: Nil) CallArrowToken[Id](toName("funcCall2"), toNumber(4) :: Nil)
) )
) )
) )

View File

@ -1,10 +1,12 @@
package aqua.parser package aqua.parser
import aqua.AquaSpec import aqua.AquaSpec
import aqua.parser.lexer.{IntoArrow, PropertyOp, VarToken} import aqua.parser.lexer.{IntoArrow, PropertyOp, PropertyToken, VarToken}
import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
import cats.Id import cats.Id
import cats.data.NonEmptyList
class IntoArrowSpec extends AnyFlatSpec with Matchers with AquaSpec { class IntoArrowSpec extends AnyFlatSpec with Matchers with AquaSpec {
import AquaSpec.* import AquaSpec.*
@ -27,7 +29,11 @@ class IntoArrowSpec extends AnyFlatSpec with Matchers with AquaSpec {
val arrowStr = "input.arrow(\"\")" val arrowStr = "input.arrow(\"\")"
val result = parseVar(arrowStr) val result = parseVar(arrowStr)
val expected = VarToken[Id](toName("input"), IntoArrow[Id](toName("arrow"), toStr("") :: Nil) :: Nil) val expected = PropertyToken[Id](
VarToken[Id](toName("input")),
NonEmptyList.one(IntoArrow[Id](toName("arrow"), toStr("") :: Nil))
)
result should be(expected) result should be(expected)
} }
} }

View File

@ -1,40 +1,95 @@
package aqua.parser package aqua.parser
import aqua.AquaSpec import aqua.AquaSpec
import aqua.parser.expr.func.{CallArrowExpr, ParExpr} import aqua.AquaSpec.*
import aqua.parser.expr.func.{CallArrowExpr, ForExpr, JoinExpr, OnExpr, ParExpr}
import aqua.parser.lexer.{CallArrowToken, Token} import aqua.parser.lexer.{CallArrowToken, Token}
import aqua.parser.lift.LiftParser.Implicits.idLiftParser
import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
import org.scalatest.Inside
import cats.{Eval, Id} import cats.{Eval, Id}
import aqua.parser.lift.LiftParser.Implicits.idLiftParser import cats.data.{Chain, NonEmptyList}
import cats.data.Chain
import cats.free.Cofree import cats.free.Cofree
class ParExprSpec extends AnyFlatSpec with Matchers with AquaSpec { class ParExprSpec extends AnyFlatSpec with Matchers with Inside with AquaSpec {
import AquaSpec._ def insidePar(str: String)(testFun: Ast.Tree[Id] => Any) =
inside(ParExpr.readLine.parseAll(str).map(_.map(_.mapK(spanToId)).forceAll)) {
case Right(tree) => testFun(tree)
}
"par" should "be parsed" in { def par(expr: Expr[Id]): Ast.Tree[Id] =
ParExpr.readLine.parseAll("par x <- y()").value.map(_.mapK(spanToId)).forceAll should be( Cofree(
Cofree[Chain, Expr[Id]]( ParExpr(Token.lift(())),
ParExpr[Id](Token.lift[Id, Unit](())),
Eval.now( Eval.now(
Chain( Chain(
Cofree[Chain, Expr[Id]]( Cofree(
CallArrowExpr( expr,
List(AquaSpec.toName("x")),
CallArrowToken(
None,
AquaSpec.toName("y"),
Nil
)
),
Eval.now(Chain.empty) Eval.now(Chain.empty)
) )
) )
) )
) )
"par" should "be parsed" in {
insidePar("par x <- y()")(
_ should be(
par(
CallArrowExpr(
List(toName("x")),
CallArrowToken(toName("y"), Nil)
)
)
)
)
insidePar("par call()")(
_ should be(
par(
CallArrowExpr(
Nil,
CallArrowToken(toName("call"), Nil)
)
)
)
)
insidePar("par on call() via relay:")(
_ should be(
par(
OnExpr(
CallArrowToken(toName("call"), Nil),
toVar("relay") :: Nil
)
)
)
)
insidePar("par join call(), x")(
_ should be(
par(
JoinExpr(
NonEmptyList.of(
CallArrowToken(toName("call"), Nil),
toVar("x")
)
)
)
)
)
insidePar("par for w <- getWorkers():")(
_ should be(
par(
ForExpr(
toName("w"),
CallArrowToken(toName("getWorkers"), Nil),
None
)
)
)
) )
} }
} }

View File

@ -7,7 +7,7 @@ import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers import org.scalatest.matchers.should.Matchers
class PushToStreamExprSpec extends AnyFlatSpec with Matchers with AquaSpec { class PushToStreamExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
import AquaSpec._ import AquaSpec.{given, *}
"assign" should "be parsed" in { "assign" should "be parsed" in {
parsePush("a <<- \"b\"") should be( parsePush("a <<- \"b\"") should be(

View File

@ -13,6 +13,7 @@ import aqua.parser.lexer.{
Name, Name,
NamedTypeToken, NamedTypeToken,
NamedValueToken, NamedValueToken,
PropertyToken,
Token, Token,
ValueToken, ValueToken,
VarToken VarToken
@ -51,12 +52,15 @@ class StructValueExprSpec extends AnyFlatSpec with Matchers with AquaSpec {
NonEmptyMap.of( NonEmptyMap.of(
"i1" -> two, "i1" -> two,
"i2" -> b, "i2" -> b,
"i3" -> CallArrowToken(None, Name[Id]("funcCall"), List(three)), "i3" -> CallArrowToken(Name[Id]("funcCall"), List(three)),
"i4" -> VarToken[Id](Name[Id]("value"), Nil) "i4" -> VarToken[Id](Name[Id]("value"))
) )
), ),
"f6" -> CallArrowToken(None, Name[Id]("funcCall"), List(one)), "f6" -> CallArrowToken(Name[Id]("funcCall"), List(one)),
"f7" -> CallArrowToken(Option(NamedTypeToken[Id]("Serv")), Name[Id]("call"), List(two)) "f7" -> PropertyToken[Id](
VarToken[Id](Name[Id]("Serv")),
NonEmptyList.one(IntoArrow[Id](Name[Id]("call"), List(two)))
)
) )
) )
) )

View File

@ -26,13 +26,13 @@ class ValueTokenComplexSpec extends AnyFlatSpec with Matchers with Inside with A
} }
} }
import AquaSpec.* import AquaSpec.{given, *}
private def variable(name: String): ValueToken[Id] = private def variable(name: String): ValueToken[Id] =
VarToken(Name(name), Nil) VarToken(Name(name))
private def func(name: String, args: List[ValueToken[Id]]): ValueToken[Id] = private def func(name: String, args: List[ValueToken[Id]]): ValueToken[Id] =
CallArrowToken(None, Name(name), args) CallArrowToken(Name(name), args)
private def literal(n: Int): ValueToken[Id] = toNumber(n) private def literal(n: Int): ValueToken[Id] = toNumber(n)

View File

@ -10,7 +10,7 @@ import org.scalatest.matchers.should.Matchers
class PropertyOpSpec extends AnyFlatSpec with Matchers with EitherValues { class PropertyOpSpec extends AnyFlatSpec with Matchers with EitherValues {
import aqua.AquaSpec._ import aqua.AquaSpec.{given, *}
"lambda ops" should "parse" in { "lambda ops" should "parse" in {
val opsP = (s: String) => PropertyOp.ops.parseAll(s).value.map(_.mapK(spanToId)) val opsP = (s: String) => PropertyOp.ops.parseAll(s).value.map(_.mapK(spanToId))

View File

@ -5,7 +5,9 @@ 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.types.LiteralType import aqua.types.LiteralType
import cats.Id import cats.Id
import cats.data.NonEmptyList
class ValueTokenSpec extends AnyFlatSpec with Matchers with EitherValues { class ValueTokenSpec extends AnyFlatSpec with Matchers with EitherValues {
@ -13,13 +15,21 @@ class ValueTokenSpec extends AnyFlatSpec with Matchers with EitherValues {
"var getter" should "parse" in { "var getter" should "parse" in {
ValueToken.`value`.parseAll("varname").value.mapK(spanToId) should be( ValueToken.`value`.parseAll("varname").value.mapK(spanToId) should be(
VarToken(Name[Id]("varname"), Nil) VarToken(Name[Id]("varname"))
) )
ValueToken.`value`.parseAll("varname.field").value.mapK(spanToId) should be( ValueToken.`value`.parseAll("varname.field").value.mapK(spanToId) should be(
VarToken(Name[Id]("varname"), IntoField[Id]("field") :: Nil) PropertyToken[Id](
VarToken(Name[Id]("varname")),
NonEmptyList.one(IntoField[Id]("field"))
) )
)
ValueToken.`value`.parseAll("varname.field.sub").value.mapK(spanToId) should be( ValueToken.`value`.parseAll("varname.field.sub").value.mapK(spanToId) should be(
VarToken(Name[Id]("varname"), IntoField[Id]("field") :: IntoField[Id]("sub") :: Nil) PropertyToken[Id](
VarToken(Name[Id]("varname")),
NonEmptyList.of(IntoField[Id]("field"), IntoField[Id]("sub"))
)
) )
} }

View File

@ -12,47 +12,43 @@ class VarLambdaSpec extends AnyFlatSpec with Matchers with EitherValues {
import aqua.AquaSpec._ import aqua.AquaSpec._
"var lambda" should "parse" in { "var lambda" should "parse" in {
val opsP = (s: String) => Name.dotted.parseAll(s).value.mapK(spanToId) val opsP = (s: String) => ValueToken.value.parseAll(s).value.mapK(spanToId)
opsP("SomeClass.some_val") should be(Name[Id]("SomeClass.some_val")) opsP("some_val") should be(
VarToken[Id](Name[Id]("some_val"))
)
opsP("some_val") should be(Name[Id]("some_val")) opsP("SOME_CONST") should be(
VarToken[Id](Name[Id]("SOME_CONST"))
)
opsP("SOME_CONST") should be(Name[Id]("SOME_CONST")) opsP("SomeClass.some_val") should be(
PropertyToken[Id](
VarToken[Id](Name[Id]("SomeClass")),
NonEmptyList.one(IntoField[Id]("some_val"))
)
)
opsP("SomeClass.SOME_CONST") should be(Name[Id]("SomeClass.SOME_CONST")) opsP("SomeClass.Some_Other_Class") should be(
} PropertyToken[Id](
VarToken[Id](Name[Id]("SomeClass")),
NonEmptyList.one(IntoField[Id]("Some_Other_Class"))
)
)
"var lambda in VarToken" should "parse" in { opsP("SomeClass.SOME_CONST") should be(
val opsP = (s: String) => ValueToken.varProperty.parseAll(s).value.mapK(spanToId) PropertyToken[Id](
VarToken[Id](Name[Id]("SomeClass")),
opsP("some_val") should be(VarToken[Id](Name[Id]("some_val"))) NonEmptyList.one(IntoField[Id]("SOME_CONST"))
)
opsP("SomeClass.SOME_CONST") should be(VarToken[Id](Name[Id]("SomeClass.SOME_CONST"))) )
}
"var lambda in value" should "parse" in {
val opsP = (s: String) => ValueToken.atom.parseAll(s).value.mapK(spanToId)
opsP("some_val") should be(VarToken[Id](Name[Id]("some_val")))
opsP("SomeClass.SOME_CONST") should be(VarToken[Id](Name[Id]("SomeClass.SOME_CONST")))
}
"var lambda in ability" should "parse" in {
val opsP = (s: String) => ValueToken.abProperty.parseAll(s).value.mapK(spanToId)
opsP("SomeClass") should be(VarToken[Id](Name[Id]("SomeClass")))
opsP("SomeClass.call()") should be( opsP("SomeClass.call()") should be(
VarToken[Id](Name[Id]("SomeClass"), IntoArrow(Name[Id]("call"), Nil) :: Nil) PropertyToken[Id](
VarToken[Id](Name[Id]("SomeClass")),
NonEmptyList.one(IntoArrow[Id](Name[Id]("call"), Nil))
)
) )
} }
"parse Class " should "parse" in {
val opsP = (s: String) => Name.cl.parseAll(s).value.mapK(spanToId)
opsP("SomeClass") should be(Name[Id]("SomeClass"))
opsP("SC") should be(Name[Id]("SC"))
}
} }

View File

@ -70,6 +70,9 @@ object Prog {
def after[Alg[_]: Monad, A](prog: A => Alg[A]): Prog[Alg, A] = def after[Alg[_]: Monad, A](prog: A => Alg[A]): Prog[Alg, A] =
RunAround(Monad[Alg].unit, (_: Unit, a: A) => prog(a)) RunAround(Monad[Alg].unit, (_: Unit, a: A) => prog(a))
def after_[Alg[_]: Monad, A](prog: => Alg[A]): Prog[Alg, A] =
after(_ => prog)
def around[Alg[_]: Monad, R, A](before: Alg[R], after: (R, A) => Alg[A]): Prog[Alg, A] = def around[Alg[_]: Monad, R, A](before: Alg[R], after: (R, A) => Alg[A]): Prog[Alg, A] =
RunAround(before, after) RunAround(before, after)

View File

@ -10,12 +10,13 @@ import aqua.semantics.rules.abilities.AbilitiesAlgebra
import aqua.semantics.rules.definitions.DefinitionsAlgebra import aqua.semantics.rules.definitions.DefinitionsAlgebra
import aqua.semantics.rules.names.NamesAlgebra import aqua.semantics.rules.names.NamesAlgebra
import aqua.semantics.rules.types.TypesAlgebra import aqua.semantics.rules.types.TypesAlgebra
import aqua.types.{ArrowType, AbilityType, Type} import aqua.types.{AbilityType, ArrowType, Type}
import cats.syntax.apply.* import cats.syntax.apply.*
import cats.syntax.flatMap.* import cats.syntax.flatMap.*
import cats.syntax.functor.* import cats.syntax.functor.*
import cats.syntax.applicative.* import cats.syntax.applicative.*
import cats.syntax.semigroupal.* import cats.syntax.semigroupal.*
import cats.syntax.traverse.*
import cats.Monad import cats.Monad
import cats.data.{NonEmptyList, NonEmptyMap} import cats.data.{NonEmptyList, NonEmptyMap}
@ -25,21 +26,15 @@ class AbilitySem[S[_]](val expr: AbilityExpr[S]) extends AnyVal {
T: TypesAlgebra[S, Alg], T: TypesAlgebra[S, Alg],
D: DefinitionsAlgebra[S, Alg] D: DefinitionsAlgebra[S, Alg]
): Prog[Alg, Raw] = { ): Prog[Alg, Raw] = {
Prog.after(_ => Prog.after_(
D.purgeDefs(expr.name).flatMap { for {
case Some(fields) => defs <- D.purgeDefs(expr.name)
val t = AbilityType(expr.name.value, fields) abType = defs.map(fields => AbilityType(expr.name.value, fields))
T.defineNamedType(expr.name, t).map { result <- abType.flatTraverse(t =>
case true => T.defineNamedType(expr.name, t)
TypeRaw( .map(Option.when(_)(TypeRaw(expr.name.value, t)))
expr.name.value, )
t } yield result.getOrElse(Raw.error("Ability types unresolved"))
): Raw
case false =>
Raw.error("Ability types unresolved")
}
case None => Raw.error("Ability types unresolved").pure[Alg]
}
) )
} }
} }

View File

@ -1,6 +1,7 @@
package aqua.semantics.expr.func package aqua.semantics.expr.func
import aqua.parser.expr.func.CallArrowExpr import aqua.parser.expr.func.CallArrowExpr
import aqua.parser.lexer.{CallArrowToken, IntoArrow, IntoField, PropertyToken, VarToken}
import aqua.raw.Raw import aqua.raw.Raw
import aqua.raw.ops.{Call, CallArrowRawTag, FuncOp} import aqua.raw.ops.{Call, CallArrowRawTag, FuncOp}
import aqua.raw.value.CallArrowRaw import aqua.raw.value.CallArrowRaw
@ -13,6 +14,9 @@ import cats.Monad
import cats.syntax.flatMap.* import cats.syntax.flatMap.*
import cats.syntax.functor.* import cats.syntax.functor.*
import cats.syntax.traverse.* import cats.syntax.traverse.*
import cats.syntax.option.*
import cats.syntax.applicative.*
import cats.syntax.comonad.*
class CallArrowSem[S[_]](val expr: CallArrowExpr[S]) extends AnyVal { class CallArrowSem[S[_]](val expr: CallArrowExpr[S]) extends AnyVal {
@ -31,12 +35,16 @@ class CallArrowSem[S[_]](val expr: CallArrowExpr[S]) extends AnyVal {
} }
} }
private def toModel[Alg[_]: Monad](implicit private def toModel[Alg[_]: Monad](using
N: NamesAlgebra[S, Alg], N: NamesAlgebra[S, Alg],
T: TypesAlgebra[S, Alg], T: TypesAlgebra[S, Alg],
V: ValuesAlgebra[S, Alg] V: ValuesAlgebra[S, Alg]
): Alg[Option[FuncOp]] = for { ): Alg[Option[FuncOp]] = for {
callArrowRaw <- V.callArrowToRaw(callArrow) callArrowRaw <- V.valueToRaw(callArrow).map {
// TODO: Refactor this to support other results
case Some(car: CallArrowRaw) => car.some
case _ => none
}
maybeOp <- callArrowRaw.traverse(car => maybeOp <- callArrowRaw.traverse(car =>
variables variables
.drop(car.baseType.codomain.length) .drop(car.baseType.codomain.length)

View File

@ -16,6 +16,7 @@ import cats.syntax.apply.*
import cats.syntax.flatMap.* import cats.syntax.flatMap.*
import cats.syntax.functor.* import cats.syntax.functor.*
import cats.syntax.traverse.* import cats.syntax.traverse.*
import cats.syntax.foldable.*
import cats.syntax.option.* import cats.syntax.option.*
import cats.instances.list.* import cats.instances.list.*
import cats.data.{NonEmptyList, NonEmptyMap} import cats.data.{NonEmptyList, NonEmptyMap}
@ -52,75 +53,57 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](implicit
case op: IntoField[S] => case op: IntoField[S] =>
T.resolveField(rootType, op) T.resolveField(rootType, op)
case op: IntoArrow[S] => case op: IntoArrow[S] =>
op.arguments for {
.map(valueToRaw) maybeArgs <- op.arguments.traverse(valueToRaw)
.sequence arrowProp <- maybeArgs.sequence.flatTraverse(
.map(_.sequence) T.resolveArrow(rootType, op, _)
.flatMap {
case None => None.pure[Alg]
case Some(arguments) => T.resolveArrow(rootType, op, arguments)
}
case op: IntoCopy[S] =>
op.fields
.map(valueToRaw)
.sequence
.map(_.sequence)
.flatMap {
case None => None.pure[Alg]
case Some(values) => T.resolveCopy(rootType, op, values)
}
case op: IntoIndex[S] =>
op.idx
.fold[Alg[Option[ValueRaw]]](Option(LiteralRaw.Zero).pure[Alg])(
valueToRaw
) )
.flatMap { } yield arrowProp
case None => None.pure[Alg] case op: IntoCopy[S] =>
case Some(values) => T.resolveIndex(rootType, op, values) for {
} maybeFields <- op.fields.traverse(valueToRaw)
copyProp <- maybeFields.sequence.flatTraverse(
T.resolveCopy(rootType, op, _)
)
} yield copyProp
case op: IntoIndex[S] =>
for {
maybeIdx <- op.idx.fold(LiteralRaw.Zero.some.pure)(valueToRaw)
idxProp <- maybeIdx.flatTraverse(
T.resolveIndex(rootType, op, _)
)
} yield idxProp
} }
def valueToRaw(v: ValueToken[S]): Alg[Option[ValueRaw]] = def valueToRaw(v: ValueToken[S]): Alg[Option[ValueRaw]] =
v match { v match {
case l: LiteralToken[S] => Some(LiteralRaw(l.value, l.ts)).pure[Alg] case l @ LiteralToken(value, t) =>
case VarToken(name, ops) => LiteralRaw(l.value, t).some.pure[Alg]
case VarToken(name) =>
N.read(name).flatMap { N.read(name).flatMap {
case Some(t) => case Some(t) =>
// Prepare property expression: take the last known type and the next op, add next op to accumulator VarRaw(name.value, t).some.pure[Alg]
ops
.foldLeft[Alg[(Option[Type], Chain[PropertyRaw])]](
(Some(t) -> Chain.empty).pure[Alg]
) { case (acc, op) =>
acc.flatMap {
// Some(rootType) means that the previous property op was resolved successfully
case (Some(rootType), prop) =>
// Resolve a single property
resolveSingleProperty(rootType, op).map {
// Property op resolved, add it to accumulator and update the last known type
case Some(p) => (Some(p.`type`), prop :+ p)
// Property op is not resolved, it's an error, stop iterations
case None => (None, Chain.empty)
}
// We have already errored, do nothing
case _ => (None, Chain.empty).pure[Alg]
}
}
.map {
// Some(_) means no errors occured
case (Some(_), property) if property.length == ops.length =>
Some(property.foldLeft[ValueRaw](VarRaw(name.value, t)) { case (v, p) =>
ApplyPropertyRaw(v, p)
})
case _ => None
}
case None => case None =>
None.pure[Alg] None.pure[Alg]
} }
case prop @ PropertyToken(value, properties) =>
prop.adjust.fold(
for {
valueRaw <- valueToRaw(value)
result <- valueRaw.flatTraverse(raw =>
properties
.foldLeftM(raw) { case (prev, op) =>
OptionT(
resolveSingleProperty(prev.`type`, op)
).map(prop => ApplyPropertyRaw(prev, prop))
}
.value
)
} yield result
)(valueToRaw)
case dvt @ NamedValueToken(typeName, fields) => case dvt @ NamedValueToken(typeName, fields) =>
T.resolveType(typeName).flatMap { T.resolveType(typeName).flatMap {
case Some(resolvedType) => case Some(resolvedType) =>
@ -153,26 +136,28 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](implicit
} }
case ct @ CollectionToken(_, values) => case ct @ CollectionToken(_, values) =>
values.traverse(valueToRaw).map(_.flatten).map(NonEmptyList.fromList).map { for {
case Some(raws) if raws.size == values.size => maybeValuesRaw <- values.traverse(valueToRaw).map(_.sequence)
raw = maybeValuesRaw.map(raws =>
NonEmptyList
.fromList(raws)
.fold(ValueRaw.Nil) { nonEmpty =>
val element = raws.map(_.`type`).reduceLeft(_ `∩` _) val element = raws.map(_.`type`).reduceLeft(_ `∩` _)
// In case we mix values of uncomparable types, intersection returns bottom, meaning "uninhabited type". // In case we mix values of uncomparable types, intersection returns bottom, meaning "uninhabited type".
// But we want to get to TopType instead: this would mean that intersection is empty, and you cannot // But we want to get to TopType instead: this would mean that intersection is empty, and you cannot
// make any decision about the structure of type, but can push anything inside // make any decision about the structure of type, but can push anything inside
val elementNotBottom = if (element == BottomType) TopType else element val elementNotBottom = if (element == BottomType) TopType else element
Some(
CollectionRaw( CollectionRaw(
raws, nonEmpty,
ct.mode match { ct.mode match {
case CollectionToken.Mode.StreamMode => StreamType(elementNotBottom) case CollectionToken.Mode.StreamMode => StreamType(elementNotBottom)
case CollectionToken.Mode.ArrayMode => ArrayType(elementNotBottom) case CollectionToken.Mode.ArrayMode => ArrayType(elementNotBottom)
case CollectionToken.Mode.OptionMode => OptionType(elementNotBottom) case CollectionToken.Mode.OptionMode => OptionType(elementNotBottom)
} }
) )
)
case _ if values.isEmpty => Some(ValueRaw.Nil)
case _ => None
} }
)
} yield raw
case ca: CallArrowToken[S] => case ca: CallArrowToken[S] =>
callArrowToRaw(ca).map(_.widen[ValueRaw]) callArrowToRaw(ca).map(_.widen[ValueRaw])
@ -316,68 +301,61 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](implicit
} }
} }
// Generate CallArrowRaw for arrow in ability private def callArrowFromAbility(
// WARNING: arguments are resolved at the end of the function and added to CallArrowRaw ab: Name[S],
def callAbType( at: AbilityType,
ab: String, funcName: Name[S]
abType: AbilityType, ): Option[CallArrowRaw] = at.arrows
ca: CallArrowToken[S] .get(funcName.value)
): Alg[Option[CallArrowRaw]] = .map(arrowType =>
abType.arrows.get(ca.funcName.value) match { CallArrowRaw.ability(
case Some(arrowType) => ab.value,
Option( funcName.value,
CallArrowRaw(None, AbilityType.fullName(ab, ca.funcName.value), Nil, arrowType, None) arrowType
).pure[Alg]
case None => None.pure[Alg]
}
def callArrowToRaw(ca: CallArrowToken[S]): Alg[Option[CallArrowRaw]] = {
for {
raw <- ca.ability
.fold(
N.readArrow(ca.funcName)
.map(
_.map(bt =>
CallArrowRaw(
ability = None,
name = ca.funcName.value,
arguments = Nil,
baseType = bt,
serviceId = None
) )
) )
private def callArrowToRaw(
callArrow: CallArrowToken[S]
): Alg[Option[CallArrowRaw]] =
for {
raw <- callArrow.ability.fold(
for {
myabeArrowType <- N.readArrow(callArrow.funcName)
} yield myabeArrowType
.map(arrowType =>
CallArrowRaw.func(
funcName = callArrow.funcName.value,
baseType = arrowType
)
) )
)(ab => )(ab =>
// Check that we have variable as ability N.read(ab.asName, mustBeDefined = false).flatMap {
N.read(ab.asName, false).flatMap { case Some(at: AbilityType) =>
case Some(at @ AbilityType(_, _)) => callArrowFromAbility(ab.asName, at, callArrow.funcName).pure
callAbType(ab.value, at, ca)
case _ => case _ =>
// Check that we have registered ability type.
// If it exists - this is ability type in file, if not - imported ability
T.getType(ab.value).flatMap { T.getType(ab.value).flatMap {
case Some(abType: AbilityType) => case Some(at: AbilityType) =>
callAbType(ab.value, abType, ca) callArrowFromAbility(ab.asName, at, callArrow.funcName).pure
case t => case _ =>
(A.getArrow(ab, ca.funcName), A.getServiceId(ab)).mapN { (A.getArrow(ab, callArrow.funcName), A.getServiceId(ab)).mapN {
case (Some(at), Right(sid)) => case (Some(at), Right(sid)) =>
// Service call, actually CallArrowRaw
CallArrowRaw( .service(
ability = Some(ab.value), abilityName = ab.value,
name = ca.funcName.value, serviceId = sid,
arguments = Nil, funcName = callArrow.funcName.value,
baseType = at, baseType = at
serviceId = Some(sid) )
).some .some
case (Some(at), Left(true)) => case (Some(at), Left(true)) =>
// Ability function call, actually CallArrowRaw
CallArrowRaw( .ability(
ability = Some(ab.value), abilityName = ab.value,
name = ca.funcName.value, funcName = callArrow.funcName.value,
arguments = Nil, baseType = at
baseType = at, )
serviceId = None .some
).some
case _ => none case _ => none
} }
} }
@ -386,9 +364,13 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](implicit
result <- raw.flatTraverse(r => result <- raw.flatTraverse(r =>
val arr = r.baseType val arr = r.baseType
for { for {
argsCheck <- T.checkArgumentsNumber(ca.funcName, arr.domain.length, ca.args.length) argsCheck <- T.checkArgumentsNumber(
callArrow.funcName,
arr.domain.length,
callArrow.args.length
)
args <- Option args <- Option
.when(argsCheck)(ca.args zip arr.domain.toList) .when(argsCheck)(callArrow.args zip arr.domain.toList)
.traverse( .traverse(
_.flatTraverse { case (tkn, tp) => _.flatTraverse { case (tkn, tp) =>
for { for {
@ -406,7 +388,6 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](implicit
} yield result } yield result
) )
} yield result } yield result
}
} }

View File

@ -244,12 +244,6 @@ class TypesInterpreter[S[_], X](implicit
else report(token, s"Cannot compare '$left' with '$right''").as(false) else report(token, s"Cannot compare '$left' with '$right''").as(false)
} }
private def extractToken(token: Token[S]) =
token match {
case VarToken(n, properties) => properties.lastOption.getOrElse(n)
case t => t
}
override def ensureTypeMatches( override def ensureTypeMatches(
token: Token[S], token: Token[S],
expected: Type, expected: Type,
@ -271,11 +265,14 @@ class TypesInterpreter[S[_], X](implicit
valueFields.toSortedMap.toList.traverse { (name, `type`) => valueFields.toSortedMap.toList.traverse { (name, `type`) =>
typeFields.lookup(name) match { typeFields.lookup(name) match {
case Some(t) => case Some(t) =>
val nextToken = extractToken(token match { val nextToken = token match {
case NamedValueToken(_, fields) => case NamedValueToken(_, fields) =>
fields.lookup(name).getOrElse(token) fields.lookup(name).getOrElse(token)
case t => t // TODO: Is it needed?
}) case PropertyToken(_, properties) =>
properties.last
case _ => token
}
ensureTypeMatches(nextToken, `type`, t) ensureTypeMatches(nextToken, `type`, t)
case None => case None =>
report( report(

View File

@ -85,8 +85,15 @@ class SemanticsSpec extends AnyFlatSpec with Matchers with Inside {
insideBody(script) { body => insideBody(script) { body =>
val arrowType = ArrowType(NilType, ConsType.cons(ScalarType.string, NilType)) val arrowType = ArrowType(NilType, ConsType.cons(ScalarType.string, NilType))
val serviceCall = val serviceCall = CallArrowRawTag
CallArrowRawTag.service(LiteralRaw.quote("srv1"), "fn1", emptyCall, "A", arrowType).leaf .service(
serviceId = LiteralRaw.quote("srv1"),
fnName = "fn1",
call = emptyCall,
name = "A",
arrowType = arrowType
)
.leaf
val expected = val expected =
ParTag.wrap( ParTag.wrap(

View File

@ -57,8 +57,8 @@ class ValuesAlgebraSpec extends AnyFlatSpec with Matchers with Inside {
def literal(value: String, `type`: LiteralType) = def literal(value: String, `type`: LiteralType) =
LiteralToken(Id(value), `type`) LiteralToken(Id(value), `type`)
def variable(name: String) = def variable(name: String): VarToken[Id] =
VarToken(Name(Id(name)), Nil) VarToken[Id](Name[Id](name))
def allPairs[A](list: List[A]): List[(A, A)] = for { def allPairs[A](list: List[A]): List[(A, A)] = for {
a <- list a <- list