Skip to content

Commit

Permalink
fix(compiler): Refactor values [fixes LNG-57] (#821)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
InversionSpaces authored Aug 15, 2023
1 parent 6146f8e commit f562bd4
Show file tree
Hide file tree
Showing 43 changed files with 885 additions and 574 deletions.
87 changes: 40 additions & 47 deletions aqua-run/src/main/scala/aqua/run/CliFunc.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,68 +4,61 @@ import aqua.parser.lexer.{CallArrowToken, CollectionToken, LiteralToken, VarToke
import aqua.parser.lift.Span
import aqua.raw.value.{CollectionRaw, LiteralRaw, ValueRaw, VarRaw}
import aqua.types.{ArrayType, BottomType}

import cats.data.{NonEmptyList, Validated, ValidatedNel}
import cats.data.Validated.{invalid, invalidNel, validNel}
import cats.{Id, ~>}
import cats.{~>, Id}
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 {

def spanToId: Span.S ~> Id = new (Span.S ~> Id) {

override def apply[A](span: Span.S[A]): Id[A] = {
span._2
}
override def apply[A](span: Span.S[A]): Id[A] = span.extract
}

def fromString(func: String): ValidatedNel[String, CliFunc] = {
CallArrowToken.callArrow.parseAll(func.trim) match {
case Right(exprSpan) =>
val expr = exprSpan.mapK(spanToId)

val argsV = expr.args.collect {
CallArrowToken.callArrow
.parseAll(func.trim)
.toValidated
.leftMap(
_.expected.map(_.context.mkString("\n"))
)
.map(_.mapK(spanToId))
.andThen(expr =>
expr.args.traverse {
case LiteralToken(value, ts) =>
validNel(LiteralRaw(value, ts))
case VarToken(name, _) =>
validNel(VarRaw(name.value, BottomType))
LiteralRaw(value, ts).valid
case VarToken(name) =>
VarRaw(name.value, BottomType).valid
case CollectionToken(_, values) =>
val hasVariables = values.exists {
case LiteralToken(_, _) => false
case _ => true
}
if (!hasVariables) {
val literals = values.collect { case LiteralToken(value, ts) =>
LiteralRaw(value, ts)
}
val hasSameTypesOrEmpty =
literals.isEmpty || literals.map(_.baseType).toSet.size == 1

if (hasSameTypesOrEmpty) {
validNel(
NonEmptyList
.fromList(literals)
.map(l => CollectionRaw(l, ArrayType(l.head.baseType)))
.getOrElse(ValueRaw.Nil)
)
} else
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."
values.traverse {
case LiteralToken(value, ts) =>
LiteralRaw(value, ts).some
case _ => none
}.toValid(
"Array elements can only be numbers, strings, or booleans."
).ensure(
"If the argument is an array, then it must contain elements of the same type."
)(_.distinctBy(_.`type`).size <= 1)
.map(
NonEmptyList
.fromList(_)
.map(l => CollectionRaw(l, ArrayType(l.head.baseType)))
.getOrElse(ValueRaw.Nil)
)
.toValidatedNel
case CallArrowToken(_, _, _) =>
invalidNel("Function calls as arguments are not supported.")
}.sequence
argsV.andThen(args =>
validNel(CliFunc(expr.funcName.value, args, expr.ability.map(_.name)))
)

case Left(err) => invalid(err.expected.map(_.context.mkString("\n")))
}
"Function calls as arguments are not supported.".invalidNel
case _ =>
"Unsupported argument.".invalidNel
}.map(args => CliFunc(expr.funcName.value, args))
)
}
}
19 changes: 7 additions & 12 deletions aqua-run/src/main/scala/aqua/run/FuncCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import aqua.model.transform.TransformConfig
import aqua.model.{AquaContext, FuncArrow}
import aqua.parser.lift.FileSpan
import aqua.run.CliFunc

import cats.data.Validated.{invalidNec, validNec}
import cats.data.{Chain, NonEmptyList, Validated, ValidatedNec}
import cats.effect.IO
Expand All @@ -19,6 +20,7 @@ import cats.syntax.functor.*
import cats.syntax.monad.*
import cats.syntax.show.*
import cats.syntax.traverse.*
import cats.syntax.option.*
import fs2.io.file.{Files, Path}
import scribe.Logging

Expand Down Expand Up @@ -84,16 +86,9 @@ object FuncCompiler {
def findFunction(
contexts: Chain[AquaContext],
func: CliFunc
): ValidatedNec[String, FuncArrow] =
func.ability
.fold(
contexts
.collectFirstSome(_.allFuncs.get(func.name))
)(ab => contexts.collectFirstSome(_.abilities.get(ab).flatMap(_.allFuncs.get(func.name))))
.map(validNec)
.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"
)
)
): ValidatedNec[String, FuncArrow] = contexts
.collectFirstSome(_.allFuncs.get(func.name))
.toValidNec(
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"
)
}
92 changes: 6 additions & 86 deletions aqua-src/antithesis.aqua
Original file line number Diff line number Diff line change
@@ -1,87 +1,7 @@
aqua Main
service Srv("srv"):
call(x: i32) -> i32

use DECLARE_CONST, decl_bar from "declare.aqua" as Declare

export SomeService, handleAb, bug214, checkAbCalls

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)
func main() -> i32:
arr = [1, 2, 3]
a <- Srv.call(0)
<- arr[Srv.call(1)]
19 changes: 7 additions & 12 deletions model/raw/src/main/scala/aqua/raw/value/PropertyRaw.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,33 +19,28 @@ case class IntoFieldRaw(name: String, `type`: Type) extends PropertyRaw {
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 map(f: ValueRaw => ValueRaw): PropertyRaw = this

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 varNames: Set[String] = Set.empty

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 {
override def map(f: ValueRaw => ValueRaw): FunctorRaw = this

Expand Down
45 changes: 45 additions & 0 deletions model/raw/src/main/scala/aqua/raw/value/ValueRaw.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package aqua.raw.value

import aqua.types.*

import cats.data.{Chain, NonEmptyList, NonEmptyMap}
import cats.Eq
import cats.syntax.option.*
import scribe.Logging

sealed trait ValueRaw {
Expand Down Expand Up @@ -263,3 +265,46 @@ case class CallArrowRaw(
s"(call ${ability.fold("")(a => s"|$a| ")} (${serviceId.fold("")(_.toString + " ")}$name) [${arguments
.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)
)

}
34 changes: 13 additions & 21 deletions parser/src/main/scala/aqua/parser/expr/ConstantExpr.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,15 @@ package aqua.parser.expr

import aqua.parser.Expr
import aqua.parser.lexer.Token.*
import aqua.parser.lexer.{
CallArrowToken,
CollectionToken,
InfixToken,
LiteralToken,
Name,
ValueToken
}
import aqua.parser.lexer.*
import aqua.parser.lift.LiftParser
import cats.Comonad
import cats.parse.Parser as P
import cats.~>
import aqua.parser.lift.Span
import aqua.parser.lift.Span.{P0ToSpan, PToSpan}
import aqua.parser.lexer.PrefixToken
import aqua.parser.lexer.VarToken

case class ConstantExpr[F[_]](
name: Name[F],
Expand All @@ -35,20 +30,17 @@ object ConstantExpr extends Expr.Leaf {
override val p: P[ConstantExpr[Span.S]] =
(((constName ~ `?`.?).with1 <* `=` <* ` `) ~ ValueToken.`value`).flatMap {
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 {
case CollectionToken(point, _) =>
P.failWith(
s"'$name' is an array, but only strings, numbers or booleans can be used"
)
case CallArrowToken(_, _, _) =>
P.failWith(
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 _ =>
case CollectionToken(point, _) => fail("a collection")
case CallArrowToken(_, _, _) => fail("a function call")
case InfixToken(_, _, _) | PrefixToken(_, _) => fail("an expression")
case PropertyToken(_, _) => fail("a property")
case NamedValueToken(_, _) => fail("an ability or data")
case LiteralToken(_, _) | VarToken(_) =>
P.pure(ConstantExpr(name, value, mark.nonEmpty))
}

Expand Down
Loading

0 comments on commit f562bd4

Please sign in to comment.