2021-07-13 07:47:31 +00:00
|
|
|
package prog8tests
|
|
|
|
|
2021-10-10 22:22:04 +00:00
|
|
|
import org.junit.jupiter.api.DynamicTest.dynamicTest
|
2021-07-13 07:47:31 +00:00
|
|
|
import org.junit.jupiter.api.Test
|
|
|
|
import org.junit.jupiter.api.TestFactory
|
2021-10-10 22:22:04 +00:00
|
|
|
import org.junit.jupiter.api.TestInstance
|
2021-07-13 07:47:31 +00:00
|
|
|
import prog8.ast.base.DataType
|
2021-10-29 21:46:51 +00:00
|
|
|
import prog8.ast.base.Position
|
2021-07-13 07:47:31 +00:00
|
|
|
import prog8.ast.expressions.*
|
|
|
|
import prog8.ast.statements.ForLoop
|
|
|
|
import prog8.ast.statements.Subroutine
|
|
|
|
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
|
2021-10-29 00:42:10 +00:00
|
|
|
import prog8.compilerinterface.toConstantIntegerRange
|
2021-10-29 03:00:30 +00:00
|
|
|
import prog8tests.ast.helpers.mapCombinations
|
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
|
2021-07-13 07:47:31 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
|
|
|
class TestCompilerOnRanges {
|
|
|
|
|
|
|
|
@Test
|
|
|
|
fun 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()
|
|
|
|
|
|
|
|
val program = result.programAst
|
2021-10-10 22:01:26 +00:00
|
|
|
val startSub = program.entrypoint
|
2021-07-13 07:47:31 +00:00
|
|
|
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
|
|
|
|
fun testFloatArrayInitializerWithRange_char_to_char() {
|
|
|
|
val platform = C64Target
|
|
|
|
val result = compileText(platform, optimize = false, """
|
|
|
|
%option enable_floats
|
|
|
|
main {
|
|
|
|
sub start() {
|
2021-09-12 16:59:53 +00:00
|
|
|
float[] cs = 'a' to 'z' ; values are computed at compile time
|
2021-07-13 07:47:31 +00:00
|
|
|
cs[0] = 23 ; keep optimizer from removing it
|
|
|
|
}
|
|
|
|
}
|
|
|
|
""").assertSuccess()
|
|
|
|
|
|
|
|
val program = result.programAst
|
2021-10-10 22:01:26 +00:00
|
|
|
val startSub = program.entrypoint
|
2021-07-13 07:47:31 +00:00
|
|
|
val decl = startSub
|
|
|
|
.statements.filterIsInstance<VarDecl>()[0]
|
|
|
|
val rhsValues = (decl.value as ArrayLiteralValue)
|
|
|
|
.value // Array<Expression>
|
|
|
|
.map { (it as NumericLiteralValue).number.toInt() }
|
2021-09-12 16:59:53 +00:00
|
|
|
val expectedStart = platform.encodeString("a", false)[0].toInt()
|
2021-07-13 07:47:31 +00:00
|
|
|
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()")
|
|
|
|
}
|
|
|
|
|
2021-07-31 12:44:02 +00:00
|
|
|
fun Subroutine.decl(varName: String): VarDecl {
|
2021-07-13 07:47:31 +00:00
|
|
|
return statements.filterIsInstance<VarDecl>()
|
|
|
|
.first { it.name == varName }
|
|
|
|
}
|
|
|
|
inline fun <reified T : Expression> VarDecl.rhs() : T {
|
|
|
|
return value as T
|
|
|
|
}
|
|
|
|
inline fun <reified T : Expression> ArrayLiteralValue.elements() : List<T> {
|
|
|
|
return value.map { it as T }
|
|
|
|
}
|
|
|
|
|
|
|
|
fun <N : Number> assertEndpoints(expFirst: N, expLast: N, actual: Iterable<N>, msg: String = ".first .. .last") {
|
|
|
|
val expectedStr = "$expFirst .. $expLast"
|
|
|
|
val actualStr = "${actual.first()} .. ${actual.last()}"
|
|
|
|
assertEquals(expectedStr, actualStr,".first .. .last")
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@TestFactory
|
|
|
|
fun floatArrayInitializerWithRange() = mapCombinations(
|
|
|
|
dim1 = listOf("", "42", "41"), // sizeInDecl
|
|
|
|
dim2 = listOf("%option enable_floats", ""), // optEnableFloats
|
|
|
|
dim3 = listOf(Cx16Target, C64Target), // platform
|
|
|
|
dim4 = listOf(false, true), // optimize
|
|
|
|
combine4 = { sizeInDecl, optEnableFloats, platform, optimize ->
|
|
|
|
val displayName =
|
2021-09-12 16:53:12 +00:00
|
|
|
"test failed for: " +
|
2021-07-13 07:47:31 +00:00
|
|
|
when (sizeInDecl) {
|
|
|
|
"" -> "no"
|
|
|
|
"42" -> "correct"
|
|
|
|
else -> "wrong"
|
|
|
|
} + " array size given" +
|
|
|
|
", " + (if (optEnableFloats == "") "without" else "with") + " %option enable_floats" +
|
|
|
|
", ${platform.name}, optimize: $optimize"
|
|
|
|
dynamicTest(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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
""")
|
2021-09-12 16:53:12 +00:00
|
|
|
if (optEnableFloats != "" && (sizeInDecl=="" || sizeInDecl=="42"))
|
2021-07-13 07:47:31 +00:00
|
|
|
result.assertSuccess()
|
2021-09-12 16:53:12 +00:00
|
|
|
else
|
2021-07-13 07:47:31 +00:00
|
|
|
result.assertFailure()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
@Test
|
|
|
|
fun testForLoopWithRange_char_to_char() {
|
|
|
|
val platform = Cx16Target
|
2021-07-17 18:45:17 +00:00
|
|
|
val result = compileText(platform, optimize = true, """
|
2021-07-13 07:47:31 +00:00
|
|
|
main {
|
|
|
|
sub start() {
|
|
|
|
ubyte i
|
|
|
|
for i in @'a' to 'f' {
|
|
|
|
i += i ; keep optimizer from removing it
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
""").assertSuccess()
|
|
|
|
|
|
|
|
val program = result.programAst
|
2021-10-10 22:01:26 +00:00
|
|
|
val startSub = program.entrypoint
|
2021-07-13 07:47:31 +00:00
|
|
|
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"
|
|
|
|
|
2021-10-29 14:20:53 +00:00
|
|
|
val intProgression = rangeExpr.toConstantIntegerRange()
|
2021-07-13 07:47:31 +00:00
|
|
|
val actualStr = "${intProgression?.first} .. ${intProgression?.last}"
|
|
|
|
assertEquals(expectedStr, actualStr,".first .. .last")
|
2021-10-29 14:20:53 +00:00
|
|
|
assertEquals(expectedEnd - expectedStart + 1, rangeExpr.size(), "rangeExpr.size()")
|
2021-07-13 07:47:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
fun testForLoopWithRange_bool_to_bool() {
|
2021-07-17 18:45:17 +00:00
|
|
|
val platform = Cx16Target
|
|
|
|
val result = compileText(platform, optimize = true, """
|
2021-07-13 07:47:31 +00:00
|
|
|
main {
|
|
|
|
sub start() {
|
|
|
|
ubyte i
|
|
|
|
for i in false to true {
|
|
|
|
i += i ; keep optimizer from removing it
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
""").assertSuccess()
|
|
|
|
|
|
|
|
val program = result.programAst
|
2021-10-10 22:01:26 +00:00
|
|
|
val startSub = program.entrypoint
|
2021-07-13 07:47:31 +00:00
|
|
|
val rangeExpr = startSub
|
|
|
|
.statements.filterIsInstance<ForLoop>()
|
|
|
|
.map { it.iterable }
|
|
|
|
.filterIsInstance<RangeExpr>()[0]
|
|
|
|
|
2021-10-29 14:20:53 +00:00
|
|
|
assertEquals(2, rangeExpr.size())
|
|
|
|
val intProgression = rangeExpr.toConstantIntegerRange()
|
2021-07-13 07:47:31 +00:00
|
|
|
assertEquals(0, intProgression?.first)
|
|
|
|
assertEquals(1, intProgression?.last)
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
fun testForLoopWithRange_ubyte_to_ubyte() {
|
2021-07-17 18:45:17 +00:00
|
|
|
val platform = Cx16Target
|
|
|
|
val result = compileText(platform, optimize = true, """
|
2021-07-13 07:47:31 +00:00
|
|
|
main {
|
|
|
|
sub start() {
|
|
|
|
ubyte i
|
|
|
|
for i in 1 to 9 {
|
|
|
|
i += i ; keep optimizer from removing it
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
""").assertSuccess()
|
|
|
|
|
|
|
|
val program = result.programAst
|
2021-10-10 22:01:26 +00:00
|
|
|
val startSub = program.entrypoint
|
2021-07-13 07:47:31 +00:00
|
|
|
val rangeExpr = startSub
|
|
|
|
.statements.filterIsInstance<ForLoop>()
|
|
|
|
.map { it.iterable }
|
|
|
|
.filterIsInstance<RangeExpr>()[0]
|
|
|
|
|
2021-10-29 14:20:53 +00:00
|
|
|
assertEquals(9, rangeExpr.size())
|
|
|
|
val intProgression = rangeExpr.toConstantIntegerRange()
|
2021-07-13 07:47:31 +00:00
|
|
|
assertEquals(1, intProgression?.first)
|
|
|
|
assertEquals(9, intProgression?.last)
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
fun testForLoopWithRange_str_downto_str() {
|
2021-10-21 23:25:26 +00:00
|
|
|
val errors = ErrorReporterForTests()
|
2021-07-31 12:44:02 +00:00
|
|
|
compileText(Cx16Target, true, """
|
2021-07-13 07:47:31 +00:00
|
|
|
main {
|
|
|
|
sub start() {
|
|
|
|
ubyte i
|
|
|
|
for i in "start" downto "end" {
|
|
|
|
i += i ; keep optimizer from removing it
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-10-21 23:25:26 +00:00
|
|
|
""", 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")
|
2021-07-13 07:47:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
fun testForLoopWithIterable_str() {
|
|
|
|
val result = compileText(Cx16Target, false, """
|
|
|
|
main {
|
|
|
|
sub start() {
|
|
|
|
ubyte i
|
|
|
|
for i in "something" {
|
|
|
|
i += i ; keep optimizer from removing it
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
""").assertSuccess()
|
|
|
|
|
|
|
|
val program = result.programAst
|
2021-10-10 22:01:26 +00:00
|
|
|
val startSub = program.entrypoint
|
2021-07-13 07:47:31 +00:00
|
|
|
val iterable = startSub
|
|
|
|
.statements.filterIsInstance<ForLoop>()
|
|
|
|
.map { it.iterable }
|
|
|
|
.filterIsInstance<IdentifierReference>()[0]
|
|
|
|
|
2021-10-12 22:21:38 +00:00
|
|
|
assertEquals(DataType.STR, iterable.inferType(program).getOr(DataType.UNDEFINED))
|
2021-07-13 07:47:31 +00:00
|
|
|
}
|
|
|
|
|
2021-10-29 21:46:51 +00:00
|
|
|
@Test
|
|
|
|
fun 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()
|
|
|
|
}
|
2021-07-13 07:47:31 +00:00
|
|
|
}
|
|
|
|
|