Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 19 commits into from
Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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