mirror of
https://github.com/fluencelabs/aqua.git
synced 2025-03-15 11:40:50 +00:00
feat(compiler)!: Make nil
option bottom [LNG-279] (#968)
* Make nil option of bottom * Fix tests * Make literals of data type * Add unit tests * Remove print
This commit is contained in:
parent
522d95b8e3
commit
11c8970fd8
@ -1,6 +1,15 @@
|
|||||||
|
aqua Stream
|
||||||
|
|
||||||
import "@fluencelabs/aqua-lib/builtin.aqua"
|
import "@fluencelabs/aqua-lib/builtin.aqua"
|
||||||
import "println.aqua"
|
import "println.aqua"
|
||||||
|
|
||||||
|
export Stringer
|
||||||
|
export checkStreams, returnStreamFromFunc
|
||||||
|
export stringEmpty, returnEmptyLiteral
|
||||||
|
export returnNilLength, stringNone
|
||||||
|
export streamFunctor, streamAssignment
|
||||||
|
export streamIntFunctor, streamJoin
|
||||||
|
|
||||||
service Stringer("stringer-id"):
|
service Stringer("stringer-id"):
|
||||||
returnString: string -> string
|
returnString: string -> string
|
||||||
|
|
||||||
@ -20,16 +29,18 @@ func returnStreamFromFunc() -> *u32:
|
|||||||
nums <- getStream()
|
nums <- getStream()
|
||||||
<- nums
|
<- nums
|
||||||
|
|
||||||
func stringNil() -> *string:
|
func stringEmpty() -> *string:
|
||||||
valueNil: *string
|
valueNil: *string
|
||||||
<- valueNil
|
<- valueNil
|
||||||
|
|
||||||
func returnNil() -> *string:
|
func returnEmpty() -> *string:
|
||||||
relayNil <- stringNil()
|
relayNil <- stringEmpty()
|
||||||
<- relayNil
|
<- relayNil
|
||||||
|
|
||||||
func returnNilLiteral() -> *string:
|
func returnEmptyLiteral() -> *string:
|
||||||
<- nil
|
empty: *string
|
||||||
|
-- TODO: return *[] here after LNG-280
|
||||||
|
<- empty
|
||||||
|
|
||||||
func returnNilLength() -> u32:
|
func returnNilLength() -> u32:
|
||||||
arr = nil
|
arr = nil
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import {
|
import {
|
||||||
checkStreams,
|
|
||||||
registerStringer,
|
registerStringer,
|
||||||
|
checkStreams,
|
||||||
returnNilLength,
|
returnNilLength,
|
||||||
returnNilLiteral,
|
returnEmptyLiteral,
|
||||||
returnStreamFromFunc,
|
returnStreamFromFunc,
|
||||||
streamAssignment,
|
streamAssignment,
|
||||||
streamFunctor,
|
streamFunctor,
|
||||||
streamIntFunctor,
|
streamIntFunctor,
|
||||||
streamJoin,
|
streamJoin,
|
||||||
stringNil,
|
stringEmpty,
|
||||||
stringNone,
|
stringNone,
|
||||||
} from "../compiled/examples/stream.js";
|
} from "../compiled/examples/stream.js";
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ export async function streamCall() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function returnNilCall() {
|
export async function returnNilCall() {
|
||||||
return stringNil();
|
return stringEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function returnNoneCall() {
|
export async function returnNoneCall() {
|
||||||
@ -47,7 +47,7 @@ export async function streamAssignmentCall() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function nilLiteralCall() {
|
export async function nilLiteralCall() {
|
||||||
return await returnNilLiteral();
|
return await returnEmptyLiteral();
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function nilLengthCall() {
|
export async function nilLengthCall() {
|
||||||
|
@ -17,8 +17,8 @@ import cats.syntax.applicative.*
|
|||||||
import cats.syntax.bifunctor.*
|
import cats.syntax.bifunctor.*
|
||||||
import cats.syntax.foldable.*
|
import cats.syntax.foldable.*
|
||||||
import cats.syntax.monoid.*
|
import cats.syntax.monoid.*
|
||||||
import cats.syntax.traverse.*
|
|
||||||
import cats.syntax.option.*
|
import cats.syntax.option.*
|
||||||
|
import cats.syntax.traverse.*
|
||||||
import scribe.Logging
|
import scribe.Logging
|
||||||
|
|
||||||
object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Logging {
|
object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Logging {
|
||||||
@ -33,19 +33,15 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi
|
|||||||
apName <- Mangler[S].findAndForbidName("literal_ap")
|
apName <- Mangler[S].findAndForbidName("literal_ap")
|
||||||
resultName <- Mangler[S].findAndForbidName(s"literal_props")
|
resultName <- Mangler[S].findAndForbidName(s"literal_props")
|
||||||
} yield {
|
} yield {
|
||||||
val cleanedType = literal.`type` match {
|
val typ = literal.`type`
|
||||||
// literals cannot be streams, use it as an array to use properties
|
val apVar = VarModel(apName, typ, properties)
|
||||||
case StreamType(el) => ArrayType(el)
|
|
||||||
case tt => tt
|
|
||||||
}
|
|
||||||
val apVar = VarModel(apName, cleanedType, properties)
|
|
||||||
val tree = inl |+| Inline.tree(
|
val tree = inl |+| Inline.tree(
|
||||||
SeqModel.wrap(
|
SeqModel.wrap(
|
||||||
FlattenModel(literal.copy(`type` = cleanedType), apVar.name).leaf,
|
FlattenModel(literal.copy(`type` = typ), apVar.name).leaf,
|
||||||
FlattenModel(apVar, resultName).leaf
|
FlattenModel(apVar, resultName).leaf
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
VarModel(resultName, properties.lastOption.map(_.`type`).getOrElse(cleanedType)) -> tree
|
VarModel(resultName, properties.lastOption.map(_.`type`).getOrElse(typ)) -> tree
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ object ValueRaw {
|
|||||||
val ParticleTtl: LiteralRaw = LiteralRaw("%ttl%", ScalarType.u32)
|
val ParticleTtl: LiteralRaw = LiteralRaw("%ttl%", ScalarType.u32)
|
||||||
val ParticleTimestamp: LiteralRaw = LiteralRaw("%timestamp%", ScalarType.u64)
|
val ParticleTimestamp: LiteralRaw = LiteralRaw("%timestamp%", ScalarType.u64)
|
||||||
|
|
||||||
val Nil: LiteralRaw = LiteralRaw("[]", StreamType(BottomType))
|
val Nil: LiteralRaw = LiteralRaw("[]", OptionType(BottomType))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type of error value
|
* Type of error value
|
||||||
@ -125,7 +125,7 @@ case class VarRaw(name: String, baseType: Type) extends ValueRaw {
|
|||||||
override def varNames: Set[String] = Set(name)
|
override def varNames: Set[String] = Set(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
case class LiteralRaw(value: String, baseType: Type) extends ValueRaw {
|
case class LiteralRaw(value: String, baseType: DataType) extends ValueRaw {
|
||||||
override def mapValues(f: ValueRaw => ValueRaw): ValueRaw = this
|
override def mapValues(f: ValueRaw => ValueRaw): ValueRaw = this
|
||||||
|
|
||||||
override def toString: String = s"{$value: ${baseType}}"
|
override def toString: String = s"{$value: ${baseType}}"
|
||||||
|
@ -6,8 +6,8 @@ import aqua.types.*
|
|||||||
|
|
||||||
import cats.Eq
|
import cats.Eq
|
||||||
import cats.data.{Chain, NonEmptyMap}
|
import cats.data.{Chain, NonEmptyMap}
|
||||||
import cats.syntax.option.*
|
|
||||||
import cats.syntax.apply.*
|
import cats.syntax.apply.*
|
||||||
|
import cats.syntax.option.*
|
||||||
import scribe.Logging
|
import scribe.Logging
|
||||||
|
|
||||||
sealed trait ValueModel {
|
sealed trait ValueModel {
|
||||||
@ -75,7 +75,7 @@ object ValueModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case class LiteralModel(value: String, `type`: Type) extends ValueModel {
|
case class LiteralModel(value: String, `type`: DataType) extends ValueModel {
|
||||||
|
|
||||||
override def toString: String = s"{$value: ${`type`}}"
|
override def toString: String = s"{$value: ${`type`}}"
|
||||||
|
|
||||||
|
@ -4,8 +4,8 @@ import aqua.parser.lift.Span.S
|
|||||||
|
|
||||||
import cats.data.NonEmptyList
|
import cats.data.NonEmptyList
|
||||||
import cats.parse.{Accumulator0, Parser as P, Parser0 as P0}
|
import cats.parse.{Accumulator0, Parser as P, Parser0 as P0}
|
||||||
import cats.{~>, Comonad, Functor}
|
|
||||||
import cats.syntax.functor.*
|
import cats.syntax.functor.*
|
||||||
|
import cats.{Comonad, Functor, ~>}
|
||||||
|
|
||||||
trait Token[F[_]] {
|
trait Token[F[_]] {
|
||||||
def as[T](v: T): F[T]
|
def as[T](v: T): F[T]
|
||||||
|
@ -16,6 +16,7 @@ import cats.data.Validated
|
|||||||
import cats.data.{Chain, EitherNec, NonEmptyChain}
|
import cats.data.{Chain, EitherNec, NonEmptyChain}
|
||||||
import cats.free.Cofree
|
import cats.free.Cofree
|
||||||
import cats.syntax.foldable.*
|
import cats.syntax.foldable.*
|
||||||
|
import cats.syntax.option.*
|
||||||
import cats.syntax.show.*
|
import cats.syntax.show.*
|
||||||
import cats.syntax.traverse.*
|
import cats.syntax.traverse.*
|
||||||
import cats.~>
|
import cats.~>
|
||||||
@ -49,11 +50,13 @@ class SemanticsSpec extends AnyFlatSpec with Matchers with Inside {
|
|||||||
inside(semantics.process(ast, init).value.run)(test)
|
inside(semantics.process(ast, init).value.run)(test)
|
||||||
}
|
}
|
||||||
|
|
||||||
def insideBody(script: String)(test: RawTag.Tree => Any): Unit =
|
def insideBody(script: String, func: Option[String] = None)(test: RawTag.Tree => Any): Unit =
|
||||||
insideResult(script) { case (_, Right(ctx)) =>
|
insideResult(script) { case (_, Right(ctx)) =>
|
||||||
inside(ctx.funcs.headOption) { case Some((_, func)) =>
|
inside(
|
||||||
test(func.arrow.body)
|
func.fold(
|
||||||
}
|
ctx.funcs.headOption.map { case (_, raw) => raw }
|
||||||
|
)(ctx.funcs.get)
|
||||||
|
) { case Some(func) => test(func.arrow.body) }
|
||||||
}
|
}
|
||||||
|
|
||||||
def insideSemErrors(script: String)(test: NonEmptyChain[SemanticError[Span.S]] => Any): Unit =
|
def insideSemErrors(script: String)(test: NonEmptyChain[SemanticError[Span.S]] => Any): Unit =
|
||||||
@ -877,7 +880,6 @@ class SemanticsSpec extends AnyFlatSpec with Matchers with Inside {
|
|||||||
|""".stripMargin
|
|""".stripMargin
|
||||||
|
|
||||||
insideBody(script) { body =>
|
insideBody(script) { body =>
|
||||||
println(body.show)
|
|
||||||
matchSubtree(body) { case (CallArrowRawTag(_, ca: CallArrowRaw), _) =>
|
matchSubtree(body) { case (CallArrowRawTag(_, ca: CallArrowRaw), _) =>
|
||||||
inside(ca.arguments) { case (c: CollectionRaw) :: Nil =>
|
inside(ca.arguments) { case (c: CollectionRaw) :: Nil =>
|
||||||
c.values.exists {
|
c.values.exists {
|
||||||
@ -892,4 +894,62 @@ class SemanticsSpec extends AnyFlatSpec with Matchers with Inside {
|
|||||||
test("[]", "")
|
test("[]", "")
|
||||||
test("?", "?")
|
test("?", "?")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it should "allow `nil` in place of an array or an option" in {
|
||||||
|
def test(p: String) = {
|
||||||
|
val script = s"""
|
||||||
|
|func length(col: ${p}string) -> u32:
|
||||||
|
| <- col.length
|
||||||
|
|
|
||||||
|
|func return() -> ${p}string:
|
||||||
|
| <- nil
|
||||||
|
|
|
||||||
|
|func test() -> u32:
|
||||||
|
| l <- length(nil)
|
||||||
|
| n <- return()
|
||||||
|
| <- l + n.length
|
||||||
|
|""".stripMargin
|
||||||
|
|
||||||
|
insideBody(script, "test".some) { body =>
|
||||||
|
matchSubtree(body) {
|
||||||
|
case (CallArrowRawTag(_, ca: CallArrowRaw), _) if ca.name == "length" =>
|
||||||
|
ca.arguments.length shouldEqual 1
|
||||||
|
}
|
||||||
|
matchSubtree(body) {
|
||||||
|
case (CallArrowRawTag(_, ca: CallArrowRaw), _) if ca.name == "return" =>
|
||||||
|
ca.arguments.length shouldEqual 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test("[]")
|
||||||
|
test("?")
|
||||||
|
}
|
||||||
|
|
||||||
|
it should "forbid `nil` in place of a stream" in {
|
||||||
|
val scriptAccept = s"""
|
||||||
|
|func length(col: *string) -> u32:
|
||||||
|
| <- col.length
|
||||||
|
|
|
||||||
|
|func test() -> u32:
|
||||||
|
| <- length(nil)
|
||||||
|
|""".stripMargin
|
||||||
|
|
||||||
|
val scriptReturn = s"""
|
||||||
|
|func return() -> *string:
|
||||||
|
| <- nil
|
||||||
|
|
|
||||||
|
|func test() -> u32:
|
||||||
|
| n <- return()
|
||||||
|
| <- n.length
|
||||||
|
|""".stripMargin
|
||||||
|
|
||||||
|
insideSemErrors(scriptAccept) { errors =>
|
||||||
|
atLeast(1, errors.toChain.toList) shouldBe a[RulesViolated[Span.S]]
|
||||||
|
}
|
||||||
|
|
||||||
|
insideSemErrors(scriptReturn) { errors =>
|
||||||
|
atLeast(1, errors.toChain.toList) shouldBe a[RulesViolated[Span.S]]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package aqua.semantics
|
package aqua.semantics
|
||||||
|
|
||||||
import aqua.parser.lexer.*
|
import aqua.parser.lexer.*
|
||||||
|
import aqua.raw.ConstantRaw
|
||||||
import aqua.raw.RawContext
|
import aqua.raw.RawContext
|
||||||
import aqua.raw.value.*
|
import aqua.raw.value.*
|
||||||
import aqua.semantics.rules.ValuesAlgebra
|
import aqua.semantics.rules.ValuesAlgebra
|
||||||
@ -14,7 +15,7 @@ import aqua.semantics.rules.types.{TypesAlgebra, TypesInterpreter}
|
|||||||
import aqua.types.*
|
import aqua.types.*
|
||||||
|
|
||||||
import cats.Id
|
import cats.Id
|
||||||
import cats.data.{NonEmptyList, NonEmptyMap, State}
|
import cats.data.{Chain, NonEmptyList, NonEmptyMap, State}
|
||||||
import monocle.syntax.all.*
|
import monocle.syntax.all.*
|
||||||
import org.scalatest.Inside
|
import org.scalatest.Inside
|
||||||
import org.scalatest.flatspec.AnyFlatSpec
|
import org.scalatest.flatspec.AnyFlatSpec
|
||||||
@ -66,9 +67,15 @@ class ValuesAlgebraSpec extends AnyFlatSpec with Matchers with Inside {
|
|||||||
b <- list
|
b <- list
|
||||||
} yield (a, b)
|
} yield (a, b)
|
||||||
|
|
||||||
def genState(vars: Map[String, Type] = Map.empty) =
|
def genState(vars: Map[String, Type] = Map.empty) = {
|
||||||
|
val init = RawContext.blank.copy(
|
||||||
|
parts = Chain
|
||||||
|
.fromSeq(ConstantRaw.defaultConstants())
|
||||||
|
.map(const => RawContext.blank -> const)
|
||||||
|
)
|
||||||
|
|
||||||
CompilerState
|
CompilerState
|
||||||
.init[Id](RawContext.blank)
|
.init[Id](init)
|
||||||
.focus(_.names)
|
.focus(_.names)
|
||||||
.modify(
|
.modify(
|
||||||
_.focus(_.stack).modify(
|
_.focus(_.stack).modify(
|
||||||
@ -78,6 +85,7 @@ class ValuesAlgebraSpec extends AnyFlatSpec with Matchers with Inside {
|
|||||||
) :: _
|
) :: _
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
def valueOfType(t: Type)(
|
def valueOfType(t: Type)(
|
||||||
varName: String,
|
varName: String,
|
||||||
@ -572,4 +580,19 @@ class ValuesAlgebraSpec extends AnyFlatSpec with Matchers with Inside {
|
|||||||
atLeast(1, st.errors.toList) shouldBe a[RulesViolated[Id]]
|
atLeast(1, st.errors.toList) shouldBe a[RulesViolated[Id]]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it should "consider `nil` of type `?⊥`" in {
|
||||||
|
val nil = variable("nil")
|
||||||
|
|
||||||
|
val alg = algebra()
|
||||||
|
|
||||||
|
val (st, res) = alg
|
||||||
|
.valueToRaw(nil)
|
||||||
|
.run(genState())
|
||||||
|
.value
|
||||||
|
|
||||||
|
inside(res) { case Some(value) =>
|
||||||
|
value.`type` shouldBe OptionType(BottomType)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user