diff --git a/codeAst/src/prog8/code/ast/AstExpressions.kt b/codeAst/src/prog8/code/ast/AstExpressions.kt index f4f0b9e68..5a599bb29 100644 --- a/codeAst/src/prog8/code/ast/AstExpressions.kt +++ b/codeAst/src/prog8/code/ast/AstExpressions.kt @@ -3,6 +3,8 @@ package prog8.code.ast import prog8.code.core.DataType import prog8.code.core.Encoding import prog8.code.core.Position +import java.util.* +import kotlin.math.round sealed class PtExpression(val type: DataType, position: Position) : PtNode(position) { @@ -26,7 +28,14 @@ class PtArrayIndexer(type: DataType, position: Position): PtExpression(type, pos } -class PtArray(type: DataType, position: Position): PtExpression(type, position) +class PtArray(type: DataType, position: Position): PtExpression(type, position) { + override fun hashCode(): Int = Objects.hash(children, type) + override fun equals(other: Any?): Boolean { + if(other==null || other !is PtArray) + return false + return type==other.type && children == other.children + } +} class PtBuiltinFunctionCall(val name: String, val void: Boolean, type: DataType, position: Position) : PtExpression(type, position) { @@ -95,9 +104,28 @@ class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position) class PtNumber(type: DataType, val number: Double, position: Position) : PtExpression(type, position) { + + init { + if(type!=DataType.FLOAT) { + val rounded = round(number) + if (rounded != number) + throw IllegalArgumentException("refused rounding of float to avoid loss of precision") + } + } + override fun printProperties() { print("$number ($type)") } + + override fun hashCode(): Int = Objects.hash(type, number) + + override fun equals(other: Any?): Boolean { + if(other==null || other !is PtNumber) + return false + return number==other.number + } + + operator fun compareTo(other: PtNumber): Int = number.compareTo(other.number) } @@ -140,6 +168,13 @@ class PtString(val value: String, val encoding: Encoding, position: Position) : override fun printProperties() { print("$encoding:\"$value\"") } + + override fun hashCode(): Int = Objects.hash(value, encoding) + override fun equals(other: Any?): Boolean { + if(other==null || other !is PtString) + return false + return value==other.value && encoding == other.encoding + } } diff --git a/compiler/test/TestPtNumber.kt b/compiler/test/TestPtNumber.kt new file mode 100644 index 000000000..aa43910df --- /dev/null +++ b/compiler/test/TestPtNumber.kt @@ -0,0 +1,156 @@ +package prog8tests + +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe +import io.kotest.matchers.string.shouldContain +import prog8.code.ast.PtArray +import prog8.code.ast.PtNumber +import prog8.code.ast.PtString +import prog8.code.core.DataType +import prog8.code.core.Encoding +import prog8.code.core.Position + + +class TestPtNumber: FunSpec({ + + fun sameValueAndType(lv1: PtNumber, lv2: PtNumber): Boolean { + return lv1.type==lv2.type && lv1==lv2 + } + + val dummyPos = Position("test", 0, 0, 0) + + test("testIdentity") { + val v = PtNumber(DataType.UWORD, 12345.0, dummyPos) + (v==v) shouldBe true + (v != v) shouldBe false + (v <= v) shouldBe true + (v >= v) shouldBe true + (v < v ) shouldBe false + (v > v ) shouldBe false + + sameValueAndType(PtNumber(DataType.UWORD, 12345.0, dummyPos), PtNumber(DataType.UWORD, 12345.0, dummyPos)) shouldBe true + } + + test("test rounding") { + shouldThrow { + PtNumber(DataType.BYTE, -2.345, dummyPos) + }.message shouldContain "refused rounding" + shouldThrow { + PtNumber(DataType.BYTE, -2.6, dummyPos) + }.message shouldContain "refused rounding" + shouldThrow { + PtNumber(DataType.UWORD, 2222.345, dummyPos) + }.message shouldContain "refused rounding" + PtNumber(DataType.UBYTE, 2.0, dummyPos).number shouldBe 2.0 + PtNumber(DataType.BYTE, -2.0, dummyPos).number shouldBe -2.0 + PtNumber(DataType.UWORD, 2222.0, dummyPos).number shouldBe 2222.0 + PtNumber(DataType.FLOAT, 123.456, dummyPos) + } + + test("testEqualsAndNotEquals") { + (PtNumber(DataType.UBYTE, 100.0, dummyPos) == PtNumber(DataType.UBYTE, 100.0, dummyPos)) shouldBe true + (PtNumber(DataType.UBYTE, 100.0, dummyPos) == PtNumber(DataType.UWORD, 100.0, dummyPos)) shouldBe true + (PtNumber(DataType.UBYTE, 100.0, dummyPos) == PtNumber(DataType.FLOAT, 100.0, dummyPos)) shouldBe true + (PtNumber(DataType.UWORD, 254.0, dummyPos) == PtNumber(DataType.UBYTE, 254.0, dummyPos)) shouldBe true + (PtNumber(DataType.UWORD, 12345.0, dummyPos) == PtNumber(DataType.UWORD, 12345.0, dummyPos)) shouldBe true + (PtNumber(DataType.UWORD, 12345.0, dummyPos) == PtNumber(DataType.FLOAT, 12345.0, dummyPos)) shouldBe true + (PtNumber(DataType.FLOAT, 100.0, dummyPos) == PtNumber(DataType.UBYTE, 100.0, dummyPos)) shouldBe true + (PtNumber(DataType.FLOAT, 22239.0, dummyPos) == PtNumber(DataType.UWORD, 22239.0, dummyPos)) shouldBe true + (PtNumber(DataType.FLOAT, 9.99, dummyPos) == PtNumber(DataType.FLOAT, 9.99, dummyPos)) shouldBe true + + sameValueAndType(PtNumber(DataType.UBYTE, 100.0, dummyPos), PtNumber(DataType.UBYTE, 100.0, dummyPos)) shouldBe true + sameValueAndType(PtNumber(DataType.UBYTE, 100.0, dummyPos), PtNumber(DataType.UWORD, 100.0, dummyPos)) shouldBe false + sameValueAndType(PtNumber(DataType.UBYTE, 100.0, dummyPos), PtNumber(DataType.FLOAT, 100.0, dummyPos)) shouldBe false + sameValueAndType(PtNumber(DataType.UWORD, 254.0, dummyPos), PtNumber(DataType.UBYTE, 254.0, dummyPos)) shouldBe false + sameValueAndType(PtNumber(DataType.UWORD, 12345.0, dummyPos), PtNumber(DataType.UWORD, 12345.0, dummyPos)) shouldBe true + sameValueAndType(PtNumber(DataType.UWORD, 12345.0, dummyPos), PtNumber(DataType.FLOAT, 12345.0, dummyPos)) shouldBe false + sameValueAndType(PtNumber(DataType.FLOAT, 100.0, dummyPos), PtNumber(DataType.UBYTE, 100.0, dummyPos)) shouldBe false + sameValueAndType(PtNumber(DataType.FLOAT, 22239.0, dummyPos), PtNumber(DataType.UWORD, 22239.0, dummyPos)) shouldBe false + sameValueAndType(PtNumber(DataType.FLOAT, 9.99, dummyPos), PtNumber(DataType.FLOAT, 9.99, dummyPos)) shouldBe true + + (PtNumber(DataType.UBYTE, 100.0, dummyPos) != PtNumber(DataType.UBYTE, 101.0, dummyPos)) shouldBe true + (PtNumber(DataType.UBYTE, 100.0, dummyPos) != PtNumber(DataType.UWORD, 101.0, dummyPos)) shouldBe true + (PtNumber(DataType.UBYTE, 100.0, dummyPos) != PtNumber(DataType.FLOAT, 101.0, dummyPos)) shouldBe true + (PtNumber(DataType.UWORD, 245.0, dummyPos) != PtNumber(DataType.UBYTE, 246.0, dummyPos)) shouldBe true + (PtNumber(DataType.UWORD, 12345.0, dummyPos) != PtNumber(DataType.UWORD, 12346.0, dummyPos)) shouldBe true + (PtNumber(DataType.UWORD, 12345.0, dummyPos) != PtNumber(DataType.FLOAT, 12346.0, dummyPos)) shouldBe true + (PtNumber(DataType.FLOAT, 9.99, dummyPos) != PtNumber(DataType.UBYTE, 9.0, dummyPos)) shouldBe true + (PtNumber(DataType.FLOAT, 9.99, dummyPos) != PtNumber(DataType.UWORD, 9.0, dummyPos)) shouldBe true + (PtNumber(DataType.FLOAT, 9.99, dummyPos) != PtNumber(DataType.FLOAT, 9.0, dummyPos)) shouldBe true + + sameValueAndType(PtNumber(DataType.UBYTE, 100.0, dummyPos), PtNumber(DataType.UBYTE, 101.0, dummyPos)) shouldBe false + sameValueAndType(PtNumber(DataType.UBYTE, 100.0, dummyPos), PtNumber(DataType.UWORD, 101.0, dummyPos)) shouldBe false + sameValueAndType(PtNumber(DataType.UBYTE, 100.0, dummyPos), PtNumber(DataType.FLOAT, 101.0, dummyPos)) shouldBe false + sameValueAndType(PtNumber(DataType.UWORD, 245.0, dummyPos), PtNumber(DataType.UBYTE, 246.0, dummyPos)) shouldBe false + sameValueAndType(PtNumber(DataType.UWORD, 12345.0, dummyPos), PtNumber(DataType.UWORD, 12346.0, dummyPos)) shouldBe false + sameValueAndType(PtNumber(DataType.UWORD, 12345.0, dummyPos), PtNumber(DataType.FLOAT, 12346.0, dummyPos)) shouldBe false + sameValueAndType(PtNumber(DataType.FLOAT, 9.99, dummyPos), PtNumber(DataType.UBYTE, 9.0, dummyPos)) shouldBe false + sameValueAndType(PtNumber(DataType.FLOAT, 9.99, dummyPos), PtNumber(DataType.UWORD, 9.0, dummyPos)) shouldBe false + sameValueAndType(PtNumber(DataType.FLOAT, 9.99, dummyPos), PtNumber(DataType.FLOAT, 9.0, dummyPos)) shouldBe false + + + } + + test("testEqualsRef") { + (PtString("hello", Encoding.PETSCII, dummyPos) == PtString("hello", Encoding.PETSCII, dummyPos)) shouldBe true + (PtString("hello", Encoding.PETSCII, dummyPos) != PtString("bye", Encoding.PETSCII, dummyPos)) shouldBe true + (PtString("hello", Encoding.SCREENCODES, dummyPos) == PtString("hello", Encoding.SCREENCODES, dummyPos)) shouldBe true + (PtString("hello", Encoding.SCREENCODES, dummyPos) != PtString("bye", Encoding.SCREENCODES, dummyPos)) shouldBe true + (PtString("hello", Encoding.SCREENCODES, dummyPos) != PtString("hello", Encoding.PETSCII, dummyPos)) shouldBe true + + val lvOne = PtNumber(DataType.UBYTE, 1.0, dummyPos) + val lvTwo = PtNumber(DataType.UBYTE, 2.0, dummyPos) + val lvThree = PtNumber(DataType.UBYTE, 3.0, dummyPos) + val lvOneR = PtNumber(DataType.UBYTE, 1.0, dummyPos) + val lvTwoR = PtNumber(DataType.UBYTE, 2.0, dummyPos) + val lvThreeR = PtNumber(DataType.UBYTE, 3.0, dummyPos) + val lvFour= PtNumber(DataType.UBYTE, 4.0, dummyPos) + val lv1 = PtArray(DataType.ARRAY_UB, dummyPos) + arrayOf(lvOne, lvTwo, lvThree).forEach { lv1.add(it) } + val lv2 = PtArray(DataType.ARRAY_UB, dummyPos) + arrayOf(lvOneR, lvTwoR, lvThreeR).forEach { lv2.add(it) } + val lv3 = PtArray(DataType.ARRAY_UB, dummyPos) + arrayOf(lvOneR, lvTwoR, lvFour).forEach { lv3.add(it) } + lv1 shouldBe lv2 + lv1 shouldNotBe lv3 + } + + test("testGreaterThan") { + (PtNumber(DataType.UBYTE, 100.0, dummyPos) > PtNumber(DataType.UBYTE, 99.0, dummyPos)) shouldBe true + (PtNumber(DataType.UWORD, 254.0, dummyPos) > PtNumber(DataType.UWORD, 253.0, dummyPos)) shouldBe true + (PtNumber(DataType.FLOAT, 100.0, dummyPos) > PtNumber(DataType.FLOAT, 99.9, dummyPos)) shouldBe true + + (PtNumber(DataType.UBYTE, 100.0, dummyPos) >= PtNumber(DataType.UBYTE, 100.0, dummyPos)) shouldBe true + (PtNumber(DataType.UWORD, 254.0, dummyPos) >= PtNumber(DataType.UWORD, 254.0, dummyPos)) shouldBe true + (PtNumber(DataType.FLOAT, 100.0, dummyPos) >= PtNumber(DataType.FLOAT, 100.0, dummyPos)) shouldBe true + + (PtNumber(DataType.UBYTE, 100.0, dummyPos) > PtNumber(DataType.UBYTE, 100.0, dummyPos)) shouldBe false + (PtNumber(DataType.UWORD, 254.0, dummyPos) > PtNumber(DataType.UWORD, 254.0, dummyPos)) shouldBe false + (PtNumber(DataType.FLOAT, 100.0, dummyPos) > PtNumber(DataType.FLOAT, 100.0, dummyPos)) shouldBe false + + (PtNumber(DataType.UBYTE, 100.0, dummyPos) >= PtNumber(DataType.UBYTE, 101.0, dummyPos)) shouldBe false + (PtNumber(DataType.UWORD, 254.0, dummyPos) >= PtNumber(DataType.UWORD, 255.0, dummyPos)) shouldBe false + (PtNumber(DataType.FLOAT, 100.0, dummyPos) >= PtNumber(DataType.FLOAT, 100.1, dummyPos)) shouldBe false + } + + test("testLessThan") { + (PtNumber(DataType.UBYTE, 100.0, dummyPos) < PtNumber(DataType.UBYTE, 101.0, dummyPos)) shouldBe true + (PtNumber(DataType.UWORD, 254.0, dummyPos) < PtNumber(DataType.UWORD, 255.0, dummyPos)) shouldBe true + (PtNumber(DataType.FLOAT, 100.0, dummyPos) < PtNumber(DataType.FLOAT, 100.1, dummyPos)) shouldBe true + + (PtNumber(DataType.UBYTE, 100.0, dummyPos) <= PtNumber(DataType.UBYTE, 100.0, dummyPos)) shouldBe true + (PtNumber(DataType.UWORD, 254.0, dummyPos) <= PtNumber(DataType.UWORD, 254.0, dummyPos)) shouldBe true + (PtNumber(DataType.FLOAT, 100.0, dummyPos) <= PtNumber(DataType.FLOAT, 100.0, dummyPos)) shouldBe true + + (PtNumber(DataType.UBYTE, 100.0, dummyPos) < PtNumber(DataType.UBYTE, 100.0, dummyPos)) shouldBe false + (PtNumber(DataType.UWORD, 254.0, dummyPos) < PtNumber(DataType.UWORD, 254.0, dummyPos)) shouldBe false + (PtNumber(DataType.FLOAT, 100.0, dummyPos) < PtNumber(DataType.FLOAT, 100.0, dummyPos)) shouldBe false + + (PtNumber(DataType.UBYTE, 100.0, dummyPos) <= PtNumber(DataType.UBYTE, 99.0, dummyPos)) shouldBe false + (PtNumber(DataType.UWORD, 254.0, dummyPos) <= PtNumber(DataType.UWORD, 253.0, dummyPos)) shouldBe false + (PtNumber(DataType.FLOAT, 100.0, dummyPos) <= PtNumber(DataType.FLOAT, 99.9, dummyPos)) shouldBe false + } + +})