prog8/compiler/test/codegeneration/TestArrayThings.kt

408 lines
12 KiB
Kotlin
Raw Normal View History

package prog8tests.codegeneration
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.PtBuiltinFunctionCall
import prog8.code.target.C64Target
import prog8.code.target.VMTarget
import prog8tests.helpers.ErrorReporterForTests
import prog8tests.helpers.compileText
import kotlin.io.path.readText
class TestArrayThings: FunSpec({
test("assign prefix var to array should compile fine and is not split into inplace array modification") {
val text = """
main {
sub start() {
byte[5] array
byte bb
array[1] = -bb
}
}
"""
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
compileText(VMTarget(), false, text, writeAssembly = true) shouldNotBe null
}
test("array in-place negation (integer types)") {
val text = """
main {
byte[10] foo
ubyte[10] foou
word[10] foow
uword[10] foowu
sub start() {
foo[1] = 42
foo[1] = -foo[1]
foow[1] = 4242
foow[1] = -foow[1]
}
}"""
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
compileText(VMTarget(), false, text, writeAssembly = true) shouldNotBe null
}
test("array in-place negation (float type) vm target") {
val text = """
%import floats
main {
float[10] flt
sub start() {
flt[1] = 42.42
flt[1] = -flt[1]
}
}"""
compileText(VMTarget(), false, text, writeAssembly = true) shouldNotBe null
}
2023-02-03 23:02:50 +00:00
test("array in-place negation (float type) 6502 target") {
val text = """
%import floats
main {
float[10] flt
sub start() {
flt[1] = 42.42
flt[1] = -flt[1]
}
}"""
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
}
test("array in-place invert") {
val text = """
main {
ubyte[10] foo
uword[10] foow
sub start() {
foo[1] = 42
foo[1] = ~foo[1]
foow[1] = 4242
foow[1] = ~foow[1]
}
}"""
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
compileText(VMTarget(), false, text, writeAssembly = true) shouldNotBe null
}
test("split only for word arrays") {
2023-12-26 17:49:01 +00:00
val srcGood = """
main {
uword[10] @split sw
word[10] @split sw2
2023-12-26 17:49:01 +00:00
sub start() {
}
}"""
compileText(C64Target(), false, srcGood, writeAssembly = false) shouldNotBe null
val srcWrong1 = """
main {
ubyte[10] @split sb
sub start() {
}
}"""
2024-09-06 20:51:26 +00:00
val errors = ErrorReporterForTests()
compileText(C64Target(), false, srcWrong1, writeAssembly = false, errors=errors) shouldBe null
errors.errors.size shouldBe 1
errors.errors[0] shouldContain "split can only be used on word arrays"
2023-12-26 17:49:01 +00:00
val srcWrong2 = """
2024-09-06 20:51:26 +00:00
%option enable_floats
2023-12-26 17:49:01 +00:00
main {
float[10] @split sf
sub start() {
}
}"""
2024-09-06 20:51:26 +00:00
errors.clear()
compileText(C64Target(), false, srcWrong2, writeAssembly = false, errors=errors) shouldBe null
errors.errors.size shouldBe 1
errors.errors[0] shouldContain "split can only be used on word arrays"
}
test("split word arrays in asm as lsb/msb") {
val text = """
main {
uword[10] @split @shared uw
word[10] @split @shared sw
uword[10] @shared normal
sub start() {
%asm {{
lda p8v_normal
lda p8v_uw_lsb
lda p8v_uw_msb
lda p8v_sw_lsb
lda p8v_sw_msb
}}
}
}"""
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
compileText(VMTarget(), false, text, writeAssembly = true) shouldNotBe null
}
2024-02-10 02:07:49 +00:00
test("split array assignments") {
val text = """
main {
sub start() {
str name1 = "name1"
str name2 = "name2"
uword[] @split names = [name1, name2, "name3"]
2024-02-10 02:07:49 +00:00
uword[] @split names2 = [name1, name2, "name3"]
2023-12-26 17:49:01 +00:00
uword[] addresses = [0,0,0]
names = [1111,2222,3333]
2023-12-26 17:49:01 +00:00
addresses = names
2024-02-10 02:07:49 +00:00
names = addresses
names2 = names
}
}"""
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
compileText(VMTarget(), false, text, writeAssembly = true) shouldNotBe null
}
test("array target with expression for index") {
val text = """
main {
sub start() {
ubyte[] array = [1,2,3]
array[cx16.r0L+1] += 42
cx16.r0L = array[cx16.r0L+1]
}
}"""
compileText(VMTarget(), false, text, writeAssembly = true) shouldNotBe null
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
}
test("split array in zeropage is okay") {
val text = """
main {
sub start() {
uword[3] @zp @split @shared thearray
}
}"""
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
val assemblyFile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".asm")
val assembly = assemblyFile.readText()
assembly shouldContain "thearray_lsb"
assembly shouldContain "thearray_msb"
}
test("indexing str or pointervar with expression") {
val text = """
main {
sub start() {
str name = "thing"
modify(name)
sub modify(str arg) {
ubyte n=1
uword pointervar
arg[n+1] = arg[1]
pointervar[n+1] = pointervar[1]
}
}
}"""
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
}
2024-01-16 19:41:53 +00:00
test("address of a uword pointer array expression") {
val src="""
main {
sub start() {
set_state(12345, 1)
}
sub set_state(uword buffer, ubyte i) {
uword addr = &buffer[i]
addr++
}
}"""
compileText(C64Target(), false, src, writeAssembly = true) shouldNotBe null
}
test("negative array index variables are not allowed, but ptr indexing is allowed") {
val src="""
main {
sub start() {
uword @shared pointer
ubyte[10] array
str name = "hello"
byte sindex
array[sindex] = 10
cx16.r0L = array[sindex]
name[sindex] = 0
cx16.r0L = name[sindex]
pointer[sindex] = 10
cx16.r0L=pointer[sindex]
}
}"""
val errors = ErrorReporterForTests()
compileText(VMTarget(), false, src, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 4
errors.errors[0] shouldContain "signed variables"
errors.errors[1] shouldContain "signed variables"
errors.errors[2] shouldContain "signed variables"
errors.errors[3] shouldContain "signed variables"
}
test("bounds checking for both positive and negative indexes, correct cases") {
val src="""
main {
sub start() {
ubyte[10] array
array[0] = 0
array[9] = 0
array[-1] = 0
array[-10] = 0
}
}"""
compileText(VMTarget(), false, src, writeAssembly = false) shouldNotBe null
}
test("bounds checking on strings, correct cases") {
val src="""
main {
sub start() {
str name = "1234567890"
name[0] = 0
name[9] = 0
}
}"""
compileText(VMTarget(), false, src, writeAssembly = false) shouldNotBe null
}
test("bounds checking for positive indexes, invalid case") {
val src="""
main {
sub start() {
ubyte[10] array
array[10] = 0
}
}"""
val errors = ErrorReporterForTests()
compileText(VMTarget(), false, src, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 1
errors.errors[0] shouldContain "out of bounds"
}
test("bounds checking for negative indexes, invalid case") {
val src="""
main {
sub start() {
ubyte[10] array
array[-11] = 0
}
}"""
val errors = ErrorReporterForTests()
compileText(VMTarget(), false, src, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 1
errors.errors[0] shouldContain "out of bounds"
}
test("bounds checking on strings invalid cases") {
val src="""
main {
sub start() {
str name = "1234567890"
name[10] = 0
name[-1] = 0
name[-11] = 0
}
}"""
val errors = ErrorReporterForTests()
compileText(VMTarget(), false, src, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 3
errors.errors[0] shouldContain "out of bounds"
errors.errors[1] shouldContain "out of bounds"
errors.errors[2] shouldContain "out of bounds"
}
test("array assignments should check for number of elements and element type correctness") {
val src="""
%option enable_floats
main {
sub start() {
ubyte[] array = 1 to 4
ubyte[] array2 = [1,2,3,4]
str[] names = ["apple", "banana", "tomato"]
array = [10,11,12,13] ; ok!
array = 20 to 23 ; ok!
names = ["x1", "x2", "x3"] ; ok!
ubyte[] array3 = [1,2,3,4000] ; error: element type
array = 10 to 15 ; error: array size
array = 1000 to 1003 ; error: element type
names = ["x1", "x2", "x3", "x4"] ; error: array size
names = [1.1, 2.2, 3.3, 4.4] ; error: array size AND element type
names = [1.1, 2.2, 999999.9] ; error: element type
names = [1.1, 2.2, 9.9] ; error: element type
}
}"""
val errors = ErrorReporterForTests()
compileText(C64Target(), false, src, writeAssembly = true, errors = errors) shouldBe null
errors.errors.size shouldBe 8
errors.errors[0] shouldContain "incompatible type"
errors.errors[1] shouldContain "array size mismatch"
errors.errors[2] shouldContain "array element out of range"
errors.errors[3] shouldContain "array size mismatch"
errors.errors[4] shouldContain "array size mismatch"
errors.errors[5] shouldContain "value has incompatible type"
errors.errors[6] shouldContain "value has incompatible type"
errors.errors[7] shouldContain "value has incompatible type"
}
test("array assignments should work via array copy call") {
val src="""
%option enable_floats
main {
sub start() {
ubyte[] array = [1,2,3]
ubyte[3] array2
float[] flarray = [1.1, 2.2, 3.3]
float[3] flarray2
word[] warray = [-2222,42,3333]
word[3] warray2
str[] names = ["apple", "banana", "tomato"]
str[3] names2
; 8 array assignments -> 8 arraycopies:
array = [8,7,6]
array = array2
flarray = [99.9, 88.8, 77.7]
flarray = flarray2
warray = [4444,5555,6666]
warray = warray2
names = ["x1", "x2", "x3"]
names = names2
}
}"""
compileText(VMTarget(), false, src, writeAssembly = true) shouldNotBe null
val result = compileText(C64Target(), false, src, writeAssembly = true)!!
val x = result.codegenAst!!.entrypoint()!!
(x.children[12] as PtBuiltinFunctionCall).name shouldBe "prog8_lib_arraycopy"
(x.children[13] as PtBuiltinFunctionCall).name shouldBe "prog8_lib_arraycopy"
(x.children[14] as PtBuiltinFunctionCall).name shouldBe "prog8_lib_arraycopy"
(x.children[15] as PtBuiltinFunctionCall).name shouldBe "prog8_lib_arraycopy"
(x.children[16] as PtBuiltinFunctionCall).name shouldBe "prog8_lib_arraycopy"
(x.children[17] as PtBuiltinFunctionCall).name shouldBe "prog8_lib_arraycopy"
(x.children[18] as PtBuiltinFunctionCall).name shouldBe "prog8_lib_arraycopy"
(x.children[19] as PtBuiltinFunctionCall).name shouldBe "prog8_lib_arraycopy"
}
})