prog8/compiler/test/TestCompilerOnRanges.kt

253 lines
9.3 KiB
Kotlin
Raw Normal View History

package prog8tests
import io.kotest.core.spec.style.FunSpec
import prog8.ast.base.DataType
import prog8.ast.base.Position
import prog8.ast.expressions.*
import prog8.ast.statements.ForLoop
import prog8.ast.statements.VarDecl
import prog8.compiler.target.C64Target
import prog8.compiler.target.Cx16Target
2021-10-29 03:28:02 +00:00
import prog8.compilerinterface.size
import prog8.compilerinterface.toConstantIntegerRange
import prog8tests.ast.helpers.cartesianProduct
2021-10-29 03:28:02 +00:00
import prog8tests.helpers.ErrorReporterForTests
import prog8tests.helpers.assertFailure
import prog8tests.helpers.assertSuccess
import prog8tests.helpers.compileText
2021-10-24 17:09:44 +00:00
import kotlin.test.assertContains
2021-10-10 22:22:04 +00:00
import kotlin.test.assertEquals
/**
* ATTENTION: this is just kludge!
* They are not really unit tests, but rather tests of the whole process,
* from source file loading all the way through to running 64tass.
*/
class TestCompilerOnRanges: FunSpec({
test("testUByteArrayInitializerWithRange_char_to_char") {
val platform = Cx16Target
val result = compileText(platform, true, """
main {
sub start() {
ubyte[] cs = @'a' to 'z' ; values are computed at compile time
cs[0] = 23 ; keep optimizer from removing it
}
}
""").assertSuccess()
2021-10-29 22:25:34 +00:00
val program = result.program
val startSub = program.entrypoint
val decl = startSub
.statements.filterIsInstance<VarDecl>()[0]
val rhsValues = (decl.value as ArrayLiteralValue)
.value // Array<Expression>
.map { (it as NumericLiteralValue).number.toInt() }
val expectedStart = platform.encodeString("a", true)[0].toInt()
val expectedEnd = platform.encodeString("z", false)[0].toInt()
val expectedStr = "$expectedStart .. $expectedEnd"
val actualStr = "${rhsValues.first()} .. ${rhsValues.last()}"
assertEquals(expectedStr, actualStr,".first .. .last")
assertEquals(expectedEnd - expectedStart + 1, rhsValues.last() - rhsValues.first() + 1, "rangeExpr.size()")
}
test("testFloatArrayInitializerWithRange_char_to_char") {
val platform = C64Target
val result = compileText(platform, optimize = false, """
%option enable_floats
main {
sub start() {
float[] cs = 'a' to 'z' ; values are computed at compile time
cs[0] = 23 ; keep optimizer from removing it
}
}
""").assertSuccess()
2021-10-29 22:25:34 +00:00
val program = result.program
val startSub = program.entrypoint
val decl = startSub
.statements.filterIsInstance<VarDecl>()[0]
val rhsValues = (decl.value as ArrayLiteralValue)
.value // Array<Expression>
.map { (it as NumericLiteralValue).number.toInt() }
val expectedStart = platform.encodeString("a", false)[0].toInt()
val expectedEnd = platform.encodeString("z", false)[0].toInt()
val expectedStr = "$expectedStart .. $expectedEnd"
val actualStr = "${rhsValues.first()} .. ${rhsValues.last()}"
assertEquals(expectedStr, actualStr,".first .. .last")
assertEquals(expectedEnd - expectedStart + 1, rhsValues.size, "rangeExpr.size()")
}
context("floatArrayInitializerWithRange") {
val combos = cartesianProduct(
listOf("", "42", "41"), // sizeInDecl
listOf("%option enable_floats", ""), // optEnableFloats
listOf(Cx16Target, C64Target), // platform
listOf(false, true) // optimize
)
combos.forEach {
val (sizeInDecl, optEnableFloats, platform, optimize) = it
val displayName =
when (sizeInDecl) {
"" -> "no"
"42" -> "correct"
else -> "wrong"
} + " array size given" +
", " + (if (optEnableFloats == "") "without" else "with") + " %option enable_floats" +
", ${platform.name}, optimize: $optimize"
test(displayName) {
val result = compileText(platform, optimize, """
$optEnableFloats
main {
sub start() {
float[$sizeInDecl] cs = 1 to 42 ; values are computed at compile time
cs[0] = 23 ; keep optimizer from removing it
}
}
""")
if (optEnableFloats != "" && (sizeInDecl=="" || sizeInDecl=="42"))
result.assertSuccess()
else
result.assertFailure()
}
}
}
test("testForLoopWithRange_char_to_char") {
val platform = Cx16Target
val result = compileText(platform, optimize = true, """
main {
sub start() {
ubyte i
for i in @'a' to 'f' {
i += i ; keep optimizer from removing it
}
}
}
""").assertSuccess()
2021-10-29 22:25:34 +00:00
val program = result.program
val startSub = program.entrypoint
val iterable = startSub
.statements.filterIsInstance<ForLoop>()
.map { it.iterable }[0]
val rangeExpr = iterable as RangeExpr
val expectedStart = platform.encodeString("a", true)[0].toInt()
val expectedEnd = platform.encodeString("f", false)[0].toInt()
val expectedStr = "$expectedStart .. $expectedEnd"
val intProgression = rangeExpr.toConstantIntegerRange()
val actualStr = "${intProgression?.first} .. ${intProgression?.last}"
assertEquals(expectedStr, actualStr,".first .. .last")
assertEquals(expectedEnd - expectedStart + 1, rangeExpr.size(), "rangeExpr.size()")
}
test("testForLoopWithRange_bool_to_bool") {
val platform = Cx16Target
val result = compileText(platform, optimize = true, """
main {
sub start() {
ubyte i
for i in false to true {
i += i ; keep optimizer from removing it
}
}
}
""").assertSuccess()
2021-10-29 22:25:34 +00:00
val program = result.program
val startSub = program.entrypoint
val rangeExpr = startSub
.statements.filterIsInstance<ForLoop>()
.map { it.iterable }
.filterIsInstance<RangeExpr>()[0]
assertEquals(2, rangeExpr.size())
val intProgression = rangeExpr.toConstantIntegerRange()
assertEquals(0, intProgression?.first)
assertEquals(1, intProgression?.last)
}
test("testForLoopWithRange_ubyte_to_ubyte") {
val platform = Cx16Target
val result = compileText(platform, optimize = true, """
main {
sub start() {
ubyte i
for i in 1 to 9 {
i += i ; keep optimizer from removing it
}
}
}
""").assertSuccess()
2021-10-29 22:25:34 +00:00
val program = result.program
val startSub = program.entrypoint
val rangeExpr = startSub
.statements.filterIsInstance<ForLoop>()
.map { it.iterable }
.filterIsInstance<RangeExpr>()[0]
assertEquals(9, rangeExpr.size())
val intProgression = rangeExpr.toConstantIntegerRange()
assertEquals(1, intProgression?.first)
assertEquals(9, intProgression?.last)
}
test("testForLoopWithRange_str_downto_str") {
val errors = ErrorReporterForTests()
compileText(Cx16Target, true, """
main {
sub start() {
ubyte i
for i in "start" downto "end" {
i += i ; keep optimizer from removing it
}
}
}
""", errors, false).assertFailure()
assertEquals(2, errors.errors.size)
2021-10-24 17:09:44 +00:00
assertContains(errors.errors[0], ".p8:5:29: range expression from value must be integer")
assertContains(errors.errors[1], ".p8:5:44: range expression to value must be integer")
}
test("testForLoopWithIterable_str") {
val result = compileText(Cx16Target, false, """
main {
sub start() {
ubyte i
for i in "something" {
i += i ; keep optimizer from removing it
}
}
}
""").assertSuccess()
2021-10-29 22:25:34 +00:00
val program = result.program
val startSub = program.entrypoint
val iterable = startSub
.statements.filterIsInstance<ForLoop>()
.map { it.iterable }
.filterIsInstance<IdentifierReference>()[0]
assertEquals(DataType.STR, iterable.inferType(program).getOr(DataType.UNDEFINED))
}
test("testRangeExprNumericSize") {
val expr = RangeExpr(
NumericLiteralValue.optimalInteger(10, Position.DUMMY),
NumericLiteralValue.optimalInteger(20, Position.DUMMY),
NumericLiteralValue.optimalInteger(2, Position.DUMMY),
Position.DUMMY)
assertEquals(6, expr.size())
expr.toConstantIntegerRange()
}
})