2024-09-08 14:03:57 +00:00
|
|
|
package prog8tests.compiler
|
2021-10-21 22:41:34 +00:00
|
|
|
|
2021-11-08 14:50:29 +00:00
|
|
|
import io.kotest.assertions.withClue
|
2021-11-07 23:16:58 +00:00
|
|
|
import io.kotest.core.spec.style.FunSpec
|
2021-11-08 14:50:29 +00:00
|
|
|
import io.kotest.matchers.shouldBe
|
|
|
|
import io.kotest.matchers.string.shouldContain
|
|
|
|
import io.kotest.matchers.types.instanceOf
|
2022-08-07 00:52:45 +00:00
|
|
|
import prog8.ast.IFunctionCall
|
2022-02-05 02:50:54 +00:00
|
|
|
import prog8.ast.expressions.IdentifierReference
|
2021-10-24 18:57:10 +00:00
|
|
|
import prog8.ast.statements.*
|
2024-09-04 19:49:47 +00:00
|
|
|
import prog8.code.core.BaseDataType
|
|
|
|
import prog8.code.core.DataTypeFull
|
2022-03-11 19:35:25 +00:00
|
|
|
import prog8.code.target.C64Target
|
2022-08-07 08:16:22 +00:00
|
|
|
import prog8.compiler.astprocessing.hasRtsInAsm
|
2021-10-21 23:25:26 +00:00
|
|
|
import prog8tests.helpers.ErrorReporterForTests
|
2021-10-21 22:41:34 +00:00
|
|
|
import prog8tests.helpers.compileText
|
|
|
|
|
|
|
|
|
2021-11-07 23:16:58 +00:00
|
|
|
class TestSubroutines: FunSpec({
|
2021-10-21 22:41:34 +00:00
|
|
|
|
2022-07-15 21:17:03 +00:00
|
|
|
test("string arg for byte param proper errormessage and subroutineptr in array too") {
|
|
|
|
val text="""
|
|
|
|
main {
|
|
|
|
sub func(ubyte bb) {
|
|
|
|
bb++
|
|
|
|
}
|
|
|
|
|
|
|
|
sub start() {
|
|
|
|
func("abc")
|
2023-05-30 19:54:19 +00:00
|
|
|
uword[] commands = ["abc", 1.234]
|
2022-07-15 21:17:03 +00:00
|
|
|
}
|
|
|
|
}"""
|
|
|
|
val errors = ErrorReporterForTests()
|
|
|
|
compileText(C64Target(), false, text, writeAssembly = true, errors=errors) shouldBe null
|
|
|
|
errors.errors.size shouldBe 2
|
2024-09-04 21:09:13 +00:00
|
|
|
errors.errors[0] shouldContain "type mismatch, was: str expected: ubyte"
|
2022-07-15 21:17:03 +00:00
|
|
|
errors.errors[1] shouldContain "initialisation value has incompatible type"
|
|
|
|
}
|
|
|
|
|
2021-11-07 23:16:58 +00:00
|
|
|
test("stringParameter") {
|
2021-10-21 22:41:34 +00:00
|
|
|
val text = """
|
|
|
|
main {
|
|
|
|
sub start() {
|
2021-10-24 18:57:10 +00:00
|
|
|
str text = "test"
|
|
|
|
|
|
|
|
asmfunc("text")
|
|
|
|
asmfunc(text)
|
|
|
|
asmfunc($2000)
|
|
|
|
func("text")
|
|
|
|
func(text)
|
|
|
|
func($2000)
|
2021-10-21 22:41:34 +00:00
|
|
|
}
|
|
|
|
|
2021-10-24 18:57:10 +00:00
|
|
|
asmsub asmfunc(str thing @AY) {
|
2021-10-21 22:41:34 +00:00
|
|
|
}
|
|
|
|
|
2021-10-24 18:57:10 +00:00
|
|
|
sub func(str thing) {
|
|
|
|
uword t2 = thing as uword
|
|
|
|
asmfunc(thing)
|
2021-10-21 22:41:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
2022-03-07 20:41:12 +00:00
|
|
|
val result = compileText(C64Target(), false, text, writeAssembly = false)!!
|
2023-02-09 00:46:23 +00:00
|
|
|
val module = result.compilerAst.toplevelModule
|
2021-10-24 18:57:10 +00:00
|
|
|
val mainBlock = module.statements.single() as Block
|
|
|
|
val asmfunc = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="asmfunc"}
|
|
|
|
val func = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="func"}
|
2021-11-08 14:50:29 +00:00
|
|
|
asmfunc.isAsmSubroutine shouldBe true
|
|
|
|
asmfunc.statements.isEmpty() shouldBe true
|
|
|
|
func.isAsmSubroutine shouldBe false
|
2021-12-07 22:21:49 +00:00
|
|
|
withClue("str param for subroutines should be changed into UWORD") {
|
2024-09-04 19:49:47 +00:00
|
|
|
asmfunc.parameters.single().type shouldBe DataTypeFull.forDt(BaseDataType.UWORD)
|
|
|
|
func.parameters.single().type shouldBe DataTypeFull.forDt(BaseDataType.UWORD)
|
2021-11-24 00:41:04 +00:00
|
|
|
func.statements.size shouldBe 4
|
|
|
|
val paramvar = func.statements[0] as VarDecl
|
|
|
|
paramvar.name shouldBe "thing"
|
2024-09-04 19:49:47 +00:00
|
|
|
paramvar.datatype shouldBe DataTypeFull.forDt(BaseDataType.UWORD)
|
2021-11-24 00:41:04 +00:00
|
|
|
}
|
2021-10-31 23:24:15 +00:00
|
|
|
val assign = func.statements[2] as Assignment
|
2021-11-08 14:50:29 +00:00
|
|
|
assign.target.identifier!!.nameInSource shouldBe listOf("t2")
|
2021-11-24 00:41:04 +00:00
|
|
|
withClue("str param in function body should have been transformed into just uword assignment") {
|
|
|
|
assign.value shouldBe instanceOf<IdentifierReference>()
|
2021-11-08 14:50:29 +00:00
|
|
|
}
|
2021-10-31 23:24:15 +00:00
|
|
|
val call = func.statements[3] as FunctionCallStatement
|
2021-11-08 14:50:29 +00:00
|
|
|
call.target.nameInSource.single() shouldBe "asmfunc"
|
|
|
|
withClue("str param in function body should not be transformed by normal compiler steps") {
|
|
|
|
call.args.single() shouldBe instanceOf<IdentifierReference>()
|
|
|
|
}
|
|
|
|
(call.args.single() as IdentifierReference).nameInSource.single() shouldBe "thing"
|
2021-10-21 22:41:34 +00:00
|
|
|
}
|
|
|
|
|
2021-11-07 23:16:58 +00:00
|
|
|
test("stringParameterAsmGen") {
|
2021-10-21 22:41:34 +00:00
|
|
|
val text = """
|
|
|
|
main {
|
|
|
|
sub start() {
|
|
|
|
str text = "test"
|
|
|
|
|
|
|
|
asmfunc("text")
|
|
|
|
asmfunc(text)
|
|
|
|
asmfunc($2000)
|
|
|
|
func("text")
|
|
|
|
func(text)
|
|
|
|
func($2000)
|
2022-02-06 03:29:36 +00:00
|
|
|
emptysub()
|
2021-10-21 22:41:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
asmsub asmfunc(str thing @AY) {
|
|
|
|
}
|
|
|
|
|
|
|
|
sub func(str thing) {
|
2021-10-24 18:57:10 +00:00
|
|
|
uword t2 = thing as uword
|
|
|
|
asmfunc(thing)
|
2021-10-21 22:41:34 +00:00
|
|
|
}
|
2022-02-06 03:29:36 +00:00
|
|
|
|
|
|
|
sub emptysub() {
|
|
|
|
}
|
2021-10-21 22:41:34 +00:00
|
|
|
}
|
|
|
|
"""
|
2022-03-07 20:41:12 +00:00
|
|
|
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
|
2023-02-09 00:46:23 +00:00
|
|
|
val module = result.compilerAst.toplevelModule
|
2021-10-21 22:41:34 +00:00
|
|
|
val mainBlock = module.statements.single() as Block
|
|
|
|
val asmfunc = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="asmfunc"}
|
|
|
|
val func = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="func"}
|
2022-02-06 03:29:36 +00:00
|
|
|
val emptysub = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="emptysub"}
|
2021-11-08 14:50:29 +00:00
|
|
|
asmfunc.isAsmSubroutine shouldBe true
|
2022-02-06 03:29:36 +00:00
|
|
|
asmfunc.statements.single() shouldBe instanceOf<InlineAssembly>()
|
|
|
|
(asmfunc.statements.single() as InlineAssembly).assembly.trim() shouldBe "rts"
|
2022-12-04 15:02:58 +00:00
|
|
|
asmfunc.hasRtsInAsm() shouldBe true
|
2021-11-08 14:50:29 +00:00
|
|
|
func.isAsmSubroutine shouldBe false
|
2021-12-07 22:21:49 +00:00
|
|
|
withClue("str param should have been changed to uword") {
|
2024-09-04 19:49:47 +00:00
|
|
|
asmfunc.parameters.single().type shouldBe DataTypeFull.forDt(BaseDataType.UWORD)
|
|
|
|
func.parameters.single().type shouldBe DataTypeFull.forDt(BaseDataType.UWORD)
|
2021-11-08 14:50:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func.statements.size shouldBe 5
|
|
|
|
func.statements[4] shouldBe instanceOf<Return>()
|
2021-10-24 18:57:10 +00:00
|
|
|
val paramvar = func.statements[0] as VarDecl
|
2021-11-08 14:50:29 +00:00
|
|
|
paramvar.name shouldBe "thing"
|
|
|
|
withClue("pre-asmgen should have changed str to uword type") {
|
2024-09-04 19:49:47 +00:00
|
|
|
paramvar.datatype shouldBe DataTypeFull.forDt(BaseDataType.UWORD)
|
2021-11-08 14:50:29 +00:00
|
|
|
}
|
2021-10-31 23:24:15 +00:00
|
|
|
val assign = func.statements[2] as Assignment
|
2021-11-08 14:50:29 +00:00
|
|
|
assign.target.identifier!!.nameInSource shouldBe listOf("t2")
|
|
|
|
withClue("str param in function body should be treated as plain uword before asmgen") {
|
|
|
|
assign.value shouldBe instanceOf<IdentifierReference>()
|
|
|
|
}
|
|
|
|
(assign.value as IdentifierReference).nameInSource.single() shouldBe "thing"
|
2021-10-31 23:24:15 +00:00
|
|
|
val call = func.statements[3] as FunctionCallStatement
|
2021-11-08 14:50:29 +00:00
|
|
|
call.target.nameInSource.single() shouldBe "asmfunc"
|
|
|
|
withClue("str param in function body should be treated as plain uword and not been transformed") {
|
|
|
|
call.args.single() shouldBe instanceOf<IdentifierReference>()
|
|
|
|
}
|
|
|
|
(call.args.single() as IdentifierReference).nameInSource.single() shouldBe "thing"
|
2022-02-06 03:29:36 +00:00
|
|
|
|
|
|
|
emptysub.statements.size shouldBe 1
|
|
|
|
emptysub.statements.single() shouldBe instanceOf<Return>()
|
2021-10-21 22:41:34 +00:00
|
|
|
}
|
|
|
|
|
2021-12-07 22:21:49 +00:00
|
|
|
test("ubyte[] array parameters") {
|
2021-10-25 21:01:07 +00:00
|
|
|
val text = """
|
|
|
|
main {
|
|
|
|
sub start() {
|
2021-12-07 22:21:49 +00:00
|
|
|
ubyte[] array = [1,2,3]
|
|
|
|
|
|
|
|
asmfunc(array)
|
|
|
|
asmfunc($2000)
|
|
|
|
asmfunc("zzzz")
|
|
|
|
func(array)
|
|
|
|
func($2000)
|
|
|
|
func("zzzz")
|
2021-10-25 21:01:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
asmsub asmfunc(ubyte[] thing @AY) {
|
|
|
|
}
|
|
|
|
|
2021-12-07 22:21:49 +00:00
|
|
|
sub func(ubyte[] thing) {
|
2021-10-25 21:01:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
|
2022-03-07 20:41:12 +00:00
|
|
|
val result = compileText(C64Target(), false, text, writeAssembly = false)!!
|
2023-02-09 00:46:23 +00:00
|
|
|
val module = result.compilerAst.toplevelModule
|
2021-12-07 22:21:49 +00:00
|
|
|
val mainBlock = module.statements.single() as Block
|
|
|
|
val asmfunc = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="asmfunc"}
|
|
|
|
val func = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="func"}
|
|
|
|
withClue("ubyte array param should have been replaced by UWORD pointer") {
|
2024-09-04 19:49:47 +00:00
|
|
|
asmfunc.parameters.single().type shouldBe DataTypeFull.forDt(BaseDataType.UWORD)
|
|
|
|
func.parameters.single().type shouldBe DataTypeFull.forDt(BaseDataType.UWORD)
|
2021-12-07 22:21:49 +00:00
|
|
|
}
|
2021-10-25 21:01:07 +00:00
|
|
|
}
|
|
|
|
|
2021-12-07 22:21:49 +00:00
|
|
|
test("not ubyte[] array parameters not allowed") {
|
2021-10-21 22:41:34 +00:00
|
|
|
val text = """
|
|
|
|
main {
|
|
|
|
sub start() {
|
|
|
|
}
|
|
|
|
|
2021-12-07 22:21:49 +00:00
|
|
|
asmsub func1(uword[] thing @AY) {
|
2021-10-21 22:41:34 +00:00
|
|
|
}
|
2021-12-07 22:21:49 +00:00
|
|
|
|
|
|
|
sub func(byte[] thing) {
|
2021-10-21 22:41:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
|
2021-12-07 22:21:49 +00:00
|
|
|
val errors = ErrorReporterForTests()
|
2022-03-07 20:41:12 +00:00
|
|
|
compileText(C64Target(), false, text, writeAssembly = false, errors=errors) shouldBe null
|
2021-12-07 22:21:49 +00:00
|
|
|
errors.errors.size shouldBe 2
|
|
|
|
errors.errors[0] shouldContain "pass-by-reference type can't be used"
|
|
|
|
errors.errors[1] shouldContain "pass-by-reference type can't be used"
|
2021-10-21 22:41:34 +00:00
|
|
|
}
|
2021-11-02 23:47:22 +00:00
|
|
|
|
2021-12-01 20:44:03 +00:00
|
|
|
test("invalid number of args check on normal subroutine") {
|
|
|
|
val text="""
|
|
|
|
main {
|
|
|
|
sub thing(ubyte a1, ubyte a2) {
|
|
|
|
}
|
|
|
|
|
|
|
|
sub start() {
|
|
|
|
thing(1)
|
|
|
|
thing(1,2)
|
|
|
|
thing(1,2,3)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
|
|
|
|
val errors = ErrorReporterForTests()
|
2022-03-07 20:41:12 +00:00
|
|
|
compileText(C64Target(), false, text, writeAssembly = false, errors=errors) shouldBe null
|
2021-12-01 20:44:03 +00:00
|
|
|
errors.errors.size shouldBe 2
|
2022-02-27 15:27:02 +00:00
|
|
|
errors.errors[0] shouldContain "7:25: invalid number of arguments"
|
|
|
|
errors.errors[1] shouldContain "9:25: invalid number of arguments"
|
2021-12-01 20:44:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
test("invalid number of args check on asm subroutine") {
|
|
|
|
val text="""
|
|
|
|
main {
|
|
|
|
asmsub thing(ubyte a1 @A, ubyte a2 @Y) {
|
|
|
|
}
|
|
|
|
|
|
|
|
sub start() {
|
|
|
|
thing(1)
|
|
|
|
thing(1,2)
|
|
|
|
thing(1,2,3)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
|
|
|
|
val errors = ErrorReporterForTests()
|
2022-03-07 20:41:12 +00:00
|
|
|
compileText(C64Target(), false, text, writeAssembly = false, errors=errors) shouldBe null
|
2021-12-01 20:44:03 +00:00
|
|
|
errors.errors.size shouldBe 2
|
2022-02-27 15:27:02 +00:00
|
|
|
errors.errors[0] shouldContain "7:25: invalid number of arguments"
|
|
|
|
errors.errors[1] shouldContain "9:25: invalid number of arguments"
|
2021-12-01 20:44:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
test("invalid number of args check on call to label and builtin func") {
|
|
|
|
val text="""
|
|
|
|
main {
|
|
|
|
label:
|
|
|
|
sub start() {
|
|
|
|
label()
|
|
|
|
label(1)
|
2022-10-22 14:35:57 +00:00
|
|
|
void cmp(22,44)
|
|
|
|
void cmp(11)
|
2021-12-01 20:44:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
|
|
|
|
val errors = ErrorReporterForTests()
|
2022-03-07 20:41:12 +00:00
|
|
|
compileText(C64Target(), false, text, writeAssembly = false, errors=errors) shouldBe null
|
2021-12-01 20:44:03 +00:00
|
|
|
errors.errors.size shouldBe 2
|
|
|
|
errors.errors[0] shouldContain "cannot use arguments"
|
|
|
|
errors.errors[1] shouldContain "invalid number of arguments"
|
|
|
|
}
|
2021-12-07 20:07:16 +00:00
|
|
|
|
|
|
|
test("fallthrough prevented") {
|
|
|
|
val text = """
|
|
|
|
main {
|
|
|
|
sub start() {
|
2021-12-15 23:56:51 +00:00
|
|
|
func(1, 2, 3)
|
2021-12-07 20:07:16 +00:00
|
|
|
|
2021-12-15 23:56:51 +00:00
|
|
|
sub func(ubyte a, ubyte b, ubyte c) {
|
2021-12-07 20:07:16 +00:00
|
|
|
a++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
2022-03-07 20:41:12 +00:00
|
|
|
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
|
2023-02-09 00:46:23 +00:00
|
|
|
val stmts = result.compilerAst.entrypoint.statements
|
2021-12-07 20:07:16 +00:00
|
|
|
|
|
|
|
stmts.last() shouldBe instanceOf<Subroutine>()
|
|
|
|
stmts.dropLast(1).last() shouldBe instanceOf<Return>() // this prevents the fallthrough
|
2022-08-07 00:52:45 +00:00
|
|
|
stmts.dropLast(2).last() shouldBe instanceOf<IFunctionCall>()
|
2021-12-07 20:07:16 +00:00
|
|
|
}
|
2021-11-07 23:16:58 +00:00
|
|
|
})
|