Skip to content

Commit

Permalink
feat(compiler)!: Make nil option bottom [LNG-279] (#968)
Browse files Browse the repository at this point in the history
* Make nil option of bottom

* Fix tests

* Make literals of data type

* Add unit tests

* Remove print
  • Loading branch information
InversionSpaces authored Nov 14, 2023
1 parent 522d95b commit 11c8970
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 32 deletions.
21 changes: 16 additions & 5 deletions integration-tests/aqua/examples/stream.aqua
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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
Expand Down
10 changes: 5 additions & 5 deletions integration-tests/src/examples/streamCall.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import {
checkStreams,
registerStringer,
checkStreams,
returnNilLength,
returnNilLiteral,
returnEmptyLiteral,
returnStreamFromFunc,
streamAssignment,
streamFunctor,
streamIntFunctor,
streamJoin,
stringNil,
stringEmpty,
stringNone,
} from "../compiled/examples/stream.js";

Expand All @@ -23,7 +23,7 @@ export async function streamCall() {
}

export async function returnNilCall() {
return stringNil();
return stringEmpty();
}

export async function returnNoneCall() {
Expand All @@ -47,7 +47,7 @@ export async function streamAssignmentCall() {
}

export async function nilLiteralCall() {
return await returnNilLiteral();
return await returnEmptyLiteral();
}

export async function nilLengthCall() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
}
}

Expand Down
4 changes: 2 additions & 2 deletions model/raw/src/main/scala/aqua/raw/value/ValueRaw.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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}}"
Expand Down
4 changes: 2 additions & 2 deletions model/src/main/scala/aqua/model/ValueModel.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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`}}"

Expand Down
2 changes: 1 addition & 1 deletion parser/src/main/scala/aqua/parser/lexer/Token.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
70 changes: 65 additions & 5 deletions semantics/src/test/scala/aqua/semantics/SemanticsSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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.~>
Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -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 {
Expand All @@ -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]]
}
}
}
29 changes: 26 additions & 3 deletions semantics/src/test/scala/aqua/semantics/ValuesAlgebraSpec.scala
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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(
Expand All @@ -78,6 +85,7 @@ class ValuesAlgebraSpec extends AnyFlatSpec with Matchers with Inside {
) :: _
)
)
}

def valueOfType(t: Type)(
varName: String,
Expand Down Expand Up @@ -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)
}
}
}

0 comments on commit 11c8970

Please sign in to comment.