mirror of
https://github.com/fluencelabs/aqua.git
synced 2024-12-04 22:50:18 +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 "println.aqua"
|
||||
|
||||
export Stringer
|
||||
export checkStreams, returnStreamFromFunc
|
||||
export stringEmpty, returnEmptyLiteral
|
||||
export returnNilLength, stringNone
|
||||
export streamFunctor, streamAssignment
|
||||
export streamIntFunctor, streamJoin
|
||||
|
||||
service Stringer("stringer-id"):
|
||||
returnString: string -> string
|
||||
|
||||
@ -20,16 +29,18 @@ func returnStreamFromFunc() -> *u32:
|
||||
nums <- getStream()
|
||||
<- nums
|
||||
|
||||
func stringNil() -> *string:
|
||||
func stringEmpty() -> *string:
|
||||
valueNil: *string
|
||||
<- valueNil
|
||||
|
||||
func returnNil() -> *string:
|
||||
relayNil <- stringNil()
|
||||
func returnEmpty() -> *string:
|
||||
relayNil <- stringEmpty()
|
||||
<- relayNil
|
||||
|
||||
func returnNilLiteral() -> *string:
|
||||
<- nil
|
||||
func returnEmptyLiteral() -> *string:
|
||||
empty: *string
|
||||
-- TODO: return *[] here after LNG-280
|
||||
<- empty
|
||||
|
||||
func returnNilLength() -> u32:
|
||||
arr = nil
|
||||
|
@ -1,14 +1,14 @@
|
||||
import {
|
||||
checkStreams,
|
||||
registerStringer,
|
||||
checkStreams,
|
||||
returnNilLength,
|
||||
returnNilLiteral,
|
||||
returnEmptyLiteral,
|
||||
returnStreamFromFunc,
|
||||
streamAssignment,
|
||||
streamFunctor,
|
||||
streamIntFunctor,
|
||||
streamJoin,
|
||||
stringNil,
|
||||
stringEmpty,
|
||||
stringNone,
|
||||
} from "../compiled/examples/stream.js";
|
||||
|
||||
@ -23,7 +23,7 @@ export async function streamCall() {
|
||||
}
|
||||
|
||||
export async function returnNilCall() {
|
||||
return stringNil();
|
||||
return stringEmpty();
|
||||
}
|
||||
|
||||
export async function returnNoneCall() {
|
||||
@ -47,7 +47,7 @@ export async function streamAssignmentCall() {
|
||||
}
|
||||
|
||||
export async function nilLiteralCall() {
|
||||
return await returnNilLiteral();
|
||||
return await returnEmptyLiteral();
|
||||
}
|
||||
|
||||
export async function nilLengthCall() {
|
||||
|
@ -17,8 +17,8 @@ import cats.syntax.applicative.*
|
||||
import cats.syntax.bifunctor.*
|
||||
import cats.syntax.foldable.*
|
||||
import cats.syntax.monoid.*
|
||||
import cats.syntax.traverse.*
|
||||
import cats.syntax.option.*
|
||||
import cats.syntax.traverse.*
|
||||
import scribe.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")
|
||||
resultName <- Mangler[S].findAndForbidName(s"literal_props")
|
||||
} yield {
|
||||
val cleanedType = literal.`type` match {
|
||||
// literals cannot be streams, use it as an array to use properties
|
||||
case StreamType(el) => ArrayType(el)
|
||||
case tt => tt
|
||||
}
|
||||
val apVar = VarModel(apName, cleanedType, properties)
|
||||
val typ = literal.`type`
|
||||
val apVar = VarModel(apName, typ, properties)
|
||||
val tree = inl |+| Inline.tree(
|
||||
SeqModel.wrap(
|
||||
FlattenModel(literal.copy(`type` = cleanedType), apVar.name).leaf,
|
||||
FlattenModel(literal.copy(`type` = typ), apVar.name).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 ParticleTimestamp: LiteralRaw = LiteralRaw("%timestamp%", ScalarType.u64)
|
||||
|
||||
val Nil: LiteralRaw = LiteralRaw("[]", StreamType(BottomType))
|
||||
val Nil: LiteralRaw = LiteralRaw("[]", OptionType(BottomType))
|
||||
|
||||
/**
|
||||
* Type of error value
|
||||
@ -125,7 +125,7 @@ case class VarRaw(name: String, baseType: Type) extends ValueRaw {
|
||||
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 toString: String = s"{$value: ${baseType}}"
|
||||
|
@ -6,8 +6,8 @@ import aqua.types.*
|
||||
|
||||
import cats.Eq
|
||||
import cats.data.{Chain, NonEmptyMap}
|
||||
import cats.syntax.option.*
|
||||
import cats.syntax.apply.*
|
||||
import cats.syntax.option.*
|
||||
import scribe.Logging
|
||||
|
||||
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`}}"
|
||||
|
||||
|
@ -4,8 +4,8 @@ import aqua.parser.lift.Span.S
|
||||
|
||||
import cats.data.NonEmptyList
|
||||
import cats.parse.{Accumulator0, Parser as P, Parser0 as P0}
|
||||
import cats.{~>, Comonad, Functor}
|
||||
import cats.syntax.functor.*
|
||||
import cats.{Comonad, Functor, ~>}
|
||||
|
||||
trait Token[F[_]] {
|
||||
def as[T](v: T): F[T]
|
||||
|
@ -16,6 +16,7 @@ import cats.data.Validated
|
||||
import cats.data.{Chain, EitherNec, NonEmptyChain}
|
||||
import cats.free.Cofree
|
||||
import cats.syntax.foldable.*
|
||||
import cats.syntax.option.*
|
||||
import cats.syntax.show.*
|
||||
import cats.syntax.traverse.*
|
||||
import cats.~>
|
||||
@ -49,11 +50,13 @@ class SemanticsSpec extends AnyFlatSpec with Matchers with Inside {
|
||||
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)) =>
|
||||
inside(ctx.funcs.headOption) { case Some((_, func)) =>
|
||||
test(func.arrow.body)
|
||||
}
|
||||
inside(
|
||||
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 =
|
||||
@ -877,7 +880,6 @@ class SemanticsSpec extends AnyFlatSpec with Matchers with Inside {
|
||||
|""".stripMargin
|
||||
|
||||
insideBody(script) { body =>
|
||||
println(body.show)
|
||||
matchSubtree(body) { case (CallArrowRawTag(_, ca: CallArrowRaw), _) =>
|
||||
inside(ca.arguments) { case (c: CollectionRaw) :: Nil =>
|
||||
c.values.exists {
|
||||
@ -892,4 +894,62 @@ class SemanticsSpec extends AnyFlatSpec with Matchers with Inside {
|
||||
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
|
||||
|
||||
import aqua.parser.lexer.*
|
||||
import aqua.raw.ConstantRaw
|
||||
import aqua.raw.RawContext
|
||||
import aqua.raw.value.*
|
||||
import aqua.semantics.rules.ValuesAlgebra
|
||||
@ -14,7 +15,7 @@ import aqua.semantics.rules.types.{TypesAlgebra, TypesInterpreter}
|
||||
import aqua.types.*
|
||||
|
||||
import cats.Id
|
||||
import cats.data.{NonEmptyList, NonEmptyMap, State}
|
||||
import cats.data.{Chain, NonEmptyList, NonEmptyMap, State}
|
||||
import monocle.syntax.all.*
|
||||
import org.scalatest.Inside
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
||||
@ -66,9 +67,15 @@ class ValuesAlgebraSpec extends AnyFlatSpec with Matchers with Inside {
|
||||
b <- list
|
||||
} 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
|
||||
.init[Id](RawContext.blank)
|
||||
.init[Id](init)
|
||||
.focus(_.names)
|
||||
.modify(
|
||||
_.focus(_.stack).modify(
|
||||
@ -78,6 +85,7 @@ class ValuesAlgebraSpec extends AnyFlatSpec with Matchers with Inside {
|
||||
) :: _
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
def valueOfType(t: Type)(
|
||||
varName: String,
|
||||
@ -572,4 +580,19 @@ class ValuesAlgebraSpec extends AnyFlatSpec with Matchers with Inside {
|
||||
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…
Reference in New Issue
Block a user