Skip to content

Commit

Permalink
RD-9239 Implement Neq operator != (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
miguelbranco80 authored Jul 12, 2023
1 parent 0838e7c commit 3de0b0a
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,7 @@ class TruffleEmitterImpl(tree: Tree)(implicit programContext: ProgramContext)
case BinaryExp(Gt(), e1, e2) => new GtNode(recurseExp(e1), recurseExp(e2))
case BinaryExp(Ge(), e1, e2) => new GeNode(recurseExp(e1), recurseExp(e2))
case BinaryExp(Eq(), e1, e2) => new EqNode(recurseExp(e1), recurseExp(e2))
case BinaryExp(Neq(), e1, e2) => NotNodeGen.create(new EqNode(recurseExp(e1), recurseExp(e2)))
case BinaryExp(Lt(), e1, e2) => new LtNode(recurseExp(e1), recurseExp(e2))
case BinaryExp(Le(), e1, e2) => new LeNode(recurseExp(e1), recurseExp(e2))
case BinaryConst(bytes) => new BinaryConstNode(bytes)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import raw.testing.tags.TruffleTests
@TruffleTests class ClosureTruffleTest extends TruffleCompilerTestContext with ClosureTest
@TruffleTests class BinaryExpOrTruffleTest extends TruffleCompilerTestContext with BinaryExpOrTest
@TruffleTests class BinaryExpEqTruffleTest extends TruffleCompilerTestContext with BinaryExpEqTest
@TruffleTests class BinaryExpNeqTruffleTest extends TruffleCompilerTestContext with BinaryExpNeqTest
@TruffleTests class ImplicitCastTruffleTest extends TruffleCompilerTestContext with ImplicitCastTest
@TruffleTests class BinaryExpModTruffleTest extends TruffleCompilerTestContext with BinaryExpModTest
@TruffleTests class BinaryExpSubTruffleTest extends TruffleCompilerTestContext with BinaryExpSubTest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ class FrontendSyntaxAnalyzer(val positions: Positions)
} | exp5

final private lazy val compOp: Parser[ComparableOp] = "==" ^^^ Eq() |
"!=" ^^^ Neq() |
"<=" ^^^ Le() |
"<" ^^^ Lt() |
">=" ^^^ Ge() |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ class ImplicitCasts(protected val parent: Phase[SourceProgram], protected val ph
val ne2 = cast(re2, t2, t).getOrElse(re2)
BinaryExp(op, ne1, ne2)
}
case _: Ge | _: Gt | _: Le | _: Lt | _: Eq => congruence(id, s, s) <* rule[BinaryExp] {
case _: Ge | _: Gt | _: Le | _: Lt | _: Eq | _: Neq => congruence(id, s, s) <* rule[BinaryExp] {
case BinaryExp(_, re1, re2) =>
val te1 = analyzer.tipe(e1)
val te2 = analyzer.tipe(e2)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1782,7 +1782,7 @@ class SemanticAnalyzer(val tree: SourceTree.SourceTree)(implicit programContext:
}

op match {
case _: Eq => expected(rql2numericsTemporalsStringsBools, None)
case _: Eq | _: Neq => expected(rql2numericsTemporalsStringsBools, None)
case _: ComparableOp => expected(rql2numericsTemporalsString, None)
case _: BooleanOp => expected(rql2bool, Some(rql2bool))
case _: Plus => expected(rql2numbersAndString, None)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,8 @@ final case class Lt() extends ComparableOp("<", 7)

final case class Eq() extends ComparableOp("==", 8)

final case class Neq() extends ComparableOp("!=", 8)

sealed abstract class BooleanOp(op: String, priority: Int) extends BinaryOp(op, priority)

final case class And() extends BooleanOp("and", 12)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/*
* Copyright 2023 RAW Labs S.A.
*
* Use of this software is governed by the Business Source License
* included in the file licenses/BSL.txt.
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0, included in the file
* licenses/APL.txt.
*/

package raw.compiler.rql2.tests.spec

import raw.TestData
import raw.compiler.rql2.tests.CompilerTestContext

trait BinaryExpNeqTest extends CompilerTestContext with CombinationSpecTestHelper {

test("1 != 1")(it => it should evaluateTo("false"))
test("1 != 2")(it => it should evaluateTo("true"))
test("2 != 1")(it => it should evaluateTo("true"))

test("Time.Build(9, 30) != Time.Build(9, 30)")(it => it should evaluateTo("false"))
test("Time.Build(9, 30) != Time.Build(10, 30)")(it => it should evaluateTo("true"))
test("Time.Build(9, 30) != 123")(it => it should typeErrorAs("time but got int"))

test("Date.Build(2022, 1, 15) != Date.Build(2022, 1, 15)")(it => it should evaluateTo("false"))
test("Date.Build(2022, 1, 15) != Timestamp.Build(2022, 1, 15, 0, 0)")(it => it should evaluateTo("false"))
test("Date.Build(2022, 1, 15) != Date.Build(2010, 1, 15)")(it => it should evaluateTo("true"))
test("Date.Build(2022, 1, 15) != Time.Build(10, 15)")(it =>
it should typeErrorAs("expected compatible with date but got time")
)

test("""Nullable.Build(1) != 1""")(_ should evaluateTo("false"))
test("""Nullable.Build(1) != 2""")(_ should evaluateTo("true"))
test("""Nullable.Build(1) != Nullable.Build(1)""")(_ should evaluateTo("false"))
test("""Nullable.Build(1) != Nullable.Build(2)""")(_ should evaluateTo("true"))

test("""1 != null""")(_ should evaluateTo("null"))
test("""null != 1""")(_ should evaluateTo("null"))
test("""(1 + null) != (1 + null)""")(_ should evaluateTo("null"))
test("""(1 + null) != Nullable.Build(1)""")(_ should evaluateTo("null"))
test("""Nullable.Build(1) != (1 + null)""")(_ should evaluateTo("null"))

test("""null != null""")(_ should (typeAs("bool") and evaluateTo("null")))

test("let t: time = Time.Build(10, 0) in t != Time.Build(10, 0)")(it => it should evaluateTo("false"))

// try support. Error propagate in all cases.
test("""1 != Success.Build(1)""")(_ should evaluateTo("false"))
test("""1 != Success.Build(2)""")(_ should evaluateTo("true"))
test("""Success.Build(1) != 1""")(_ should evaluateTo("false"))
test("""Success.Build(1) != 2""")(_ should evaluateTo("true"))
test("""Success.Build(1) != Success.Build(1)""")(_ should evaluateTo("false"))
test("""Success.Build(1) != Success.Build(2)""")(_ should evaluateTo("true"))

test("""1 + Error.Build("argh!") != 1 + Error.Build("gasp!")""")(_ should runErrorAs("argh!"))
test("""1 + Error.Build("gasp!") != 1 + Error.Build("argh!")""")(_ should runErrorAs("gasp!"))
test("""Error.Build("argh!") != Error.Build("argh!")""")(_ should (typeAs("bool") and runErrorAs("argh!")))
test("""Error.Build("gasp!") != Error.Build("argh!")""")(_ should (typeAs("bool") and runErrorAs("gasp!")))
test("""1 != Error.Build("argh!")""")(_ should runErrorAs("argh!"))
test("""Error.Build("argh!") != 1""")(_ should runErrorAs("argh!"))

test("""let x: int = Success.Build(1) in 1 != x""")(_ should evaluateTo("false"))
test("""let x: int = Success.Build(2) in 1 != x""")(_ should evaluateTo("true"))
test("""let x: int = Success.Build(1) in x != 1""")(_ should evaluateTo("false"))
test("""let x: int = Success.Build(1) in x != 2""")(_ should evaluateTo("true"))
test("""let x: int = Success.Build(1), y: int = Success.Build(1) in y != x""")(_ should evaluateTo("false"))
test("""let x: int = Success.Build(1), y: int = Success.Build(2) in x != y""")(_ should evaluateTo("true"))
test("""let x: int = Success.Build(1), y: int = Error.Build("argh!") in y != x""")(_ should runErrorAs("argh!"))
test("""let x: int = Success.Build(1), y: int = Error.Build("argh!") in x != y""")(_ should runErrorAs("argh!"))
test("""let x: int = Error.Build("argh!"), y: int = Error.Build("gasp!") in x != y""")(_ should runErrorAs("argh!"))
test("""let x: int = Error.Build("argh!"), y: int = Error.Build("gasp!") in y != x""")(_ should runErrorAs("gasp!"))
test("""let x: int = Error.Build("argh!") in 1 != x""")(_ should runErrorAs("argh!"))
test("""let x: int = Error.Build("argh!") in x != 1""")(_ should runErrorAs("argh!"))

val numbers = Table(
"numbers",
TestValue("byte", "1b", "2b"),
TestValue("short", "Short.From(1)", "Short.From(2)"),
TestValue("int", "1", "2"),
TestValue("long", "1l", "2l"),
TestValue("float", "1f", "2f"),
TestValue("double", "1d", "2d"),
TestValue("decimal", "Decimal.From(1)", "Decimal.From(2)")
)

val nonComparable = Table(
"non-comparable",
TestValue("record(a: int)", "{a: 1}", "{a: 2}"),
TestValue("collection(int)", "Collection.Build(1, 2, 3)", "Collection.Build(4, 5, 6)"),
TestValue("list(int)", "[1, 2, 3]", "[4, 5, 6]"),
TestValue("binary", """Binary.FromString("Hello") """, """Binary.FromString("World") """)
)

val dateTimestamp = Table(
"date-timestamp",
TestValue("date", "Date.Build(2023, 2, 6)", "Date.Build(2022, 1, 5)"),
TestValue("timestamp", "Timestamp.Build(2023, 2, 6, 0, 0)", "Timestamp.Build(2022, 1, 5, 0, 0)")
)

val nonNumbers = Table(
"non-numbers",
TestValue("string", """ "hello!" """, """ "world!" """),
TestValue("bool", "true", "false"),
TestValue("time", "Time.Build(10, 0)", "Time.Build(9, 0)"),
TestValue("interval", "Interval.Build(years=1, months=2, days=3)", "Interval.Build(hours=3, minutes=2, seconds=1)")
) ++ dateTimestamp

test(" number != number") { _ =>
forAll(combinations(numbers, numbers)) {
case (n1, n2) =>
TestData(s"${n1.v1} != ${n2.v1}") should evaluateTo("false")
TestData(s"${n1.v1} != ${n2.v2}") should evaluateTo("true")
}
}

test("date timestamp != date timestamp") { _ =>
forAll(combinations(dateTimestamp, dateTimestamp)) {
case (n1, n2) =>
TestData(s"${n1.v1} != ${n2.v1}") should evaluateTo("false")
TestData(s"${n1.v1} != ${n2.v2}") should evaluateTo("true")
}
}

test("number != non-number") { _ =>
forAll(combinations(numbers, nonNumbers)) {
case (n, x) =>
TestData(s"${n.v1} != ${x.v1}") should typeErrorAs(s"expected compatible with ${n.tipe} but got ${x.tipe}")
TestData(s"${x.v1} != ${n.v1}") should typeErrorAs(s"expected compatible with ${x.tipe} but got ${n.tipe}")
}
}

test("number != non-comparable") { _ =>
forAll(combinations(numbers, nonComparable)) {
case (n, x) =>
TestData(s"${n.v1} != ${x.v1}") should typeErrorAs(s"expected compatible with ${n.tipe} but got ${x.tipe}")
TestData(s"${x.v1} != ${n.v1}") should typeErrorAs(s"expected compatible with ${x.tipe} but got ${n.tipe}")
}
}

test("non-number != non-number") { _ =>
forAll(nonNumbers) { x =>
TestData(s"${x.v1} != ${x.v1}") should evaluateTo("false")
TestData(s"${x.v1} != ${x.v2}") should evaluateTo("true")
}
}

test("non-comparable != non-comparable") { _ =>
forAll(nonComparable) { x =>
TestData(s"${x.v1} != ${x.v1}") should typeErrorAs(
s"expected either number or temporal or bool or string but got ${x.tipe}"
)
}
}

test("error != value") { _ =>
forAll(nonNumbers ++ numbers)(x => TestData(s"""${x.v1} != Error.Build("argh!")""") should runErrorAs("argh!"))
}

test("success comparable != comparable") { _ =>
forAll(nonNumbers ++ numbers) { x =>
TestData(s"""let x: ${x.tipe} = ${x.v1} in x != ${x.v1}""") should evaluateTo("false")
TestData(s"""let x: ${x.tipe} = ${x.v1} in x != ${x.v2}""") should evaluateTo("true")
}
}
}

0 comments on commit 3de0b0a

Please sign in to comment.