prog8/compiler/test/TestNumbers.kt
2023-12-03 02:45:26 +01:00

210 lines
10 KiB
Kotlin

package prog8tests
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.doubles.plusOrMinus
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.string.shouldContain
import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.Assignment
import prog8.code.core.DataType
import prog8.code.core.InternalCompilerException
import prog8.code.core.Position
import prog8.code.core.toHex
import prog8.code.target.C64Target
import prog8.code.target.cbm.Mflpt5
import prog8tests.helpers.ErrorReporterForTests
import prog8tests.helpers.compileText
class TestNumbers: FunSpec({
test("testToHex") {
0.toHex() shouldBe "0"
1.toHex() shouldBe "1"
1.234.toHex() shouldBe "1"
10.toHex() shouldBe "10"
10.99.toHex() shouldBe "10"
15.toHex() shouldBe "15"
16.toHex() shouldBe "\$10"
255.toHex() shouldBe "\$ff"
256.toHex() shouldBe "\$0100"
20060.toHex() shouldBe "\$4e5c"
50050.toHex() shouldBe "\$c382"
65535.toHex() shouldBe "\$ffff"
65535L.toHex() shouldBe "\$ffff"
0.toHex() shouldBe "0"
(-1).toHex() shouldBe "-1"
(-1.234).toHex() shouldBe "-1"
(-10).toHex() shouldBe "-10"
(-10.99).toHex() shouldBe "-10"
(-15).toHex() shouldBe "-15"
(-16).toHex() shouldBe "-\$10"
(-255).toHex() shouldBe "-\$ff"
(-256).toHex() shouldBe "-\$0100"
(-20060).toHex() shouldBe "-\$4e5c"
(-50050).toHex() shouldBe "-\$c382"
(-65535).toHex() shouldBe "-\$ffff"
(-65535L).toHex() shouldBe "-\$ffff"
shouldThrow<IllegalArgumentException> { 65536.toHex() }
shouldThrow<IllegalArgumentException> { 65536L.toHex() }
}
test("testFloatToMflpt5") {
Mflpt5.fromNumber(0) shouldBe Mflpt5(0x00u, 0x00u, 0x00u, 0x00u, 0x00u)
Mflpt5.fromNumber(3.141592653) shouldBe Mflpt5(0x82u, 0x49u, 0x0Fu, 0xDAu, 0xA1u)
Mflpt5.fromNumber(3.141592653589793) shouldBe Mflpt5(0x82u, 0x49u, 0x0Fu, 0xDAu, 0xA2u)
Mflpt5.fromNumber(32768) shouldBe Mflpt5(0x90u, 0x00u, 0x00u, 0x00u, 0x00u)
Mflpt5.fromNumber(-32768) shouldBe Mflpt5(0x90u, 0x80u, 0x00u, 0x00u, 0x00u)
Mflpt5.fromNumber(1) shouldBe Mflpt5(0x81u, 0x00u, 0x00u, 0x00u, 0x00u)
Mflpt5.fromNumber(0.7071067812) shouldBe Mflpt5(0x80u, 0x35u, 0x04u, 0xF3u, 0x34u)
Mflpt5.fromNumber(0.7071067811865476) shouldBe Mflpt5(0x80u, 0x35u, 0x04u, 0xF3u, 0x33u)
Mflpt5.fromNumber(1.4142135624) shouldBe Mflpt5(0x81u, 0x35u, 0x04u, 0xF3u, 0x34u)
Mflpt5.fromNumber(1.4142135623730951) shouldBe Mflpt5(0x81u, 0x35u, 0x04u, 0xF3u, 0x33u)
Mflpt5.fromNumber(-.5) shouldBe Mflpt5(0x80u, 0x80u, 0x00u, 0x00u, 0x00u)
Mflpt5.fromNumber(0.69314718061) shouldBe Mflpt5(0x80u, 0x31u, 0x72u, 0x17u, 0xF8u)
Mflpt5.fromNumber(0.6931471805599453) shouldBe Mflpt5(0x80u, 0x31u, 0x72u, 0x17u, 0xF7u)
Mflpt5.fromNumber(10) shouldBe Mflpt5(0x84u, 0x20u, 0x00u, 0x00u, 0x00u)
Mflpt5.fromNumber(1000000000) shouldBe Mflpt5(0x9Eu, 0x6Eu, 0x6Bu, 0x28u, 0x00u)
Mflpt5.fromNumber(.5) shouldBe Mflpt5(0x80u, 0x00u, 0x00u, 0x00u, 0x00u)
Mflpt5.fromNumber(1.4426950408889634) shouldBe Mflpt5(0x81u, 0x38u, 0xAAu, 0x3Bu, 0x29u)
Mflpt5.fromNumber(1.5707963267948966) shouldBe Mflpt5(0x81u, 0x49u, 0x0Fu, 0xDAu, 0xA2u)
Mflpt5.fromNumber(6.283185307179586) shouldBe Mflpt5(0x83u, 0x49u, 0x0Fu, 0xDAu, 0xA2u)
Mflpt5.fromNumber(.25) shouldBe Mflpt5(0x7Fu, 0x00u, 0x00u, 0x00u, 0x00u)
Mflpt5.fromNumber(123.45678e22) shouldBe Mflpt5(0xd1u, 0x02u, 0xb7u, 0x06u, 0xfbu)
Mflpt5.fromNumber(-123.45678e-22) shouldBe Mflpt5(0x3eu, 0xe9u, 0x34u, 0x09u, 0x1bu)
}
test("testFloatRange") {
Mflpt5.fromNumber(Mflpt5.FLOAT_MAX_POSITIVE) shouldBe Mflpt5(0xffu, 0x7fu, 0xffu, 0xffu, 0xffu)
Mflpt5.fromNumber(Mflpt5.FLOAT_MAX_NEGATIVE) shouldBe Mflpt5(0xffu, 0xffu, 0xffu, 0xffu, 0xffu)
Mflpt5.fromNumber(1.7e-38) shouldBe Mflpt5(0x03u, 0x39u, 0x1du, 0x15u, 0x63u)
Mflpt5.fromNumber(1.7e-39) shouldBe Mflpt5(0x00u, 0x00u, 0x00u, 0x00u, 0x00u)
Mflpt5.fromNumber(-1.7e-38) shouldBe Mflpt5(0x03u, 0xb9u, 0x1du, 0x15u, 0x63u)
Mflpt5.fromNumber(-1.7e-39) shouldBe Mflpt5(0x00u, 0x00u, 0x00u, 0x00u, 0x00u)
shouldThrow<InternalCompilerException> { Mflpt5.fromNumber(1.7014118346e+38) }
shouldThrow<InternalCompilerException> { Mflpt5.fromNumber(-1.7014118346e+38) }
shouldThrow<InternalCompilerException> { Mflpt5.fromNumber(1.7014118347e+38) }
shouldThrow<InternalCompilerException> { Mflpt5.fromNumber(-1.7014118347e+38) }
}
test("testMflpt5ToFloat") {
val epsilon=0.000000001
Mflpt5(0x00u, 0x00u, 0x00u, 0x00u, 0x00u).toDouble() shouldBe 0.0
Mflpt5(0x82u, 0x49u, 0x0Fu, 0xDAu, 0xA1u).toDouble() shouldBe(3.141592653 plusOrMinus epsilon)
Mflpt5(0x82u, 0x49u, 0x0Fu, 0xDAu, 0xA2u).toDouble() shouldBe(3.141592653589793 plusOrMinus epsilon)
Mflpt5(0x90u, 0x00u, 0x00u, 0x00u, 0x00u).toDouble() shouldBe 32768.0
Mflpt5(0x90u, 0x80u, 0x00u, 0x00u, 0x00u).toDouble() shouldBe -32768.0
Mflpt5(0x81u, 0x00u, 0x00u, 0x00u, 0x00u).toDouble() shouldBe 1.0
Mflpt5(0x80u, 0x35u, 0x04u, 0xF3u, 0x34u).toDouble() shouldBe(0.7071067812 plusOrMinus epsilon)
Mflpt5(0x80u, 0x35u, 0x04u, 0xF3u, 0x33u).toDouble() shouldBe(0.7071067811865476 plusOrMinus epsilon)
Mflpt5(0x81u, 0x35u, 0x04u, 0xF3u, 0x34u).toDouble() shouldBe(1.4142135624 plusOrMinus epsilon)
Mflpt5(0x81u, 0x35u, 0x04u, 0xF3u, 0x33u).toDouble() shouldBe(1.4142135623730951 plusOrMinus epsilon)
Mflpt5(0x80u, 0x80u, 0x00u, 0x00u, 0x00u).toDouble() shouldBe -.5
Mflpt5(0x80u, 0x31u, 0x72u, 0x17u, 0xF8u).toDouble() shouldBe(0.69314718061 plusOrMinus epsilon)
Mflpt5(0x80u, 0x31u, 0x72u, 0x17u, 0xF7u).toDouble() shouldBe(0.6931471805599453 plusOrMinus epsilon)
Mflpt5(0x84u, 0x20u, 0x00u, 0x00u, 0x00u).toDouble() shouldBe 10.0
Mflpt5(0x9Eu, 0x6Eu, 0x6Bu, 0x28u, 0x00u).toDouble() shouldBe 1000000000.0
Mflpt5(0x80u, 0x00u, 0x00u, 0x00u, 0x00u).toDouble() shouldBe .5
Mflpt5(0x81u, 0x38u, 0xAAu, 0x3Bu, 0x29u).toDouble() shouldBe(1.4426950408889634 plusOrMinus epsilon)
Mflpt5(0x81u, 0x49u, 0x0Fu, 0xDAu, 0xA2u).toDouble() shouldBe(1.5707963267948966 plusOrMinus epsilon)
Mflpt5(0x83u, 0x49u, 0x0Fu, 0xDAu, 0xA2u).toDouble() shouldBe(6.283185307179586 plusOrMinus epsilon)
Mflpt5(0x7Fu, 0x00u, 0x00u, 0x00u, 0x00u).toDouble() shouldBe .25
Mflpt5(0xd1u, 0x02u, 0xb7u, 0x06u, 0xfbu).toDouble() shouldBe(123.45678e22 plusOrMinus 1.0e15)
Mflpt5(0x3eu, 0xe9u, 0x34u, 0x09u, 0x1bu).toDouble() shouldBe(-123.45678e-22 plusOrMinus epsilon)
}
test("implicit float conversion warning if enabled") {
val src="""
%option enable_floats
main {
sub start() {
uword xx = 10
if xx+99 == 1.23456
xx++
if xx+99 == 1234567.8
xx++
}
}
"""
val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
compileText(C64Target(), true, src, writeAssembly = false, errors=errors) shouldNotBe null
errors.errors.size shouldBe 0
errors.warnings.size shouldBe 2
errors.warnings[0] shouldContain "converted to float"
errors.warnings[1] shouldContain "converted to float"
}
test("implicit float conversion error if not enabled") {
val src="""
main {
sub start() {
uword xx = 10
if xx+99 == 1.23456
xx++
if xx+99 == 1234567.8
xx++
}
}
"""
val errors = ErrorReporterForTests()
compileText(C64Target(), true, src, writeAssembly = false, errors=errors) shouldBe null
errors.errors.size shouldBe 2
errors.warnings.size shouldBe 0
errors.errors[0] shouldContain "converted to float"
errors.errors[1] shouldContain "converted to float"
}
test("out of range number assignments") {
val src="""
main {
sub start() {
uword @shared qq = ${'$'}2ff33
cx16.r0 = ${'$'}1fc0f
}
}
"""
val errors = ErrorReporterForTests()
compileText(C64Target(), true, src, writeAssembly = false, errors=errors) shouldBe null
errors.errors.size shouldBe 2
errors.warnings.size shouldBe 0
errors.errors[0] shouldContain "out of range"
errors.errors[1] shouldContain "out of range"
}
test("big numbers okay in const expressions if result fits") {
val src="""
main {
sub start() {
uword @shared qq = ${'$'}2ff33 >> 4
cx16.r0 = ${'$'}1fc0f >> 4
}
}
"""
compileText(C64Target(), true, src, writeAssembly = false) shouldNotBe null
}
test("signed negative numbers cast to unsigned allowed") {
val src="""
main {
sub start() {
uword uw1 = -32768
uword uw = -1
ubyte ub = -1
uw = -2 as uword
ub = -2 as ubyte
}
}
"""
val result = compileText(C64Target(), false, src, writeAssembly = false)!!
val statements = result.compilerAst.entrypoint.statements
statements.size shouldBe 8
(statements[1] as Assignment).value shouldBe NumericLiteral(DataType.UWORD, 32768.0, Position.DUMMY)
(statements[3] as Assignment).value shouldBe NumericLiteral(DataType.UWORD, 65535.0, Position.DUMMY)
(statements[5] as Assignment).value shouldBe NumericLiteral(DataType.UBYTE, 255.0, Position.DUMMY)
(statements[6] as Assignment).value shouldBe NumericLiteral(DataType.UWORD, 65534.0, Position.DUMMY)
(statements[7] as Assignment).value shouldBe NumericLiteral(DataType.UBYTE, 254.0, Position.DUMMY)
}
})