2021-10-21 22:41:34 +00:00
|
|
|
package prog8tests
|
|
|
|
|
|
|
|
import org.junit.jupiter.api.Disabled
|
|
|
|
import org.junit.jupiter.api.Test
|
|
|
|
import org.junit.jupiter.api.TestInstance
|
|
|
|
import prog8.ast.base.DataType
|
2021-11-02 23:47:22 +00:00
|
|
|
import prog8.ast.expressions.*
|
2021-10-24 18:57:10 +00:00
|
|
|
import prog8.ast.statements.*
|
2021-10-21 22:41:34 +00:00
|
|
|
import prog8.compiler.target.C64Target
|
2021-10-21 23:25:26 +00:00
|
|
|
import prog8tests.helpers.ErrorReporterForTests
|
2021-10-21 22:41:34 +00:00
|
|
|
import prog8tests.helpers.assertFailure
|
|
|
|
import prog8tests.helpers.assertSuccess
|
|
|
|
import prog8tests.helpers.compileText
|
2021-11-02 23:47:22 +00:00
|
|
|
import kotlin.test.*
|
2021-10-21 22:41:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
|
|
|
class TestSubroutines {
|
|
|
|
|
|
|
|
@Test
|
2021-10-24 18:57:10 +00:00
|
|
|
fun 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
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
2021-10-24 18:57:10 +00:00
|
|
|
val result = compileText(C64Target, false, text, writeAssembly = false).assertSuccess()
|
2021-10-29 22:25:34 +00:00
|
|
|
val module = result.program.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"}
|
|
|
|
assertTrue(asmfunc.isAsmSubroutine)
|
|
|
|
assertEquals(DataType.STR, asmfunc.parameters.single().type)
|
|
|
|
assertTrue(asmfunc.statements.isEmpty())
|
|
|
|
assertFalse(func.isAsmSubroutine)
|
|
|
|
assertEquals(DataType.STR, func.parameters.single().type)
|
2021-10-31 23:24:15 +00:00
|
|
|
assertEquals(4, func.statements.size)
|
2021-10-24 18:57:10 +00:00
|
|
|
val paramvar = func.statements[0] as VarDecl
|
|
|
|
assertEquals("thing", paramvar.name)
|
|
|
|
assertEquals(DataType.STR, paramvar.datatype)
|
2021-10-31 23:24:15 +00:00
|
|
|
val assign = func.statements[2] as Assignment
|
|
|
|
assertEquals(listOf("t2"), assign.target.identifier!!.nameInSource)
|
|
|
|
assertTrue(assign.value is TypecastExpression, "str param in function body should not be transformed by normal compiler steps")
|
|
|
|
assertEquals(DataType.UWORD, (assign.value as TypecastExpression).type)
|
|
|
|
val call = func.statements[3] as FunctionCallStatement
|
2021-10-24 18:57:10 +00:00
|
|
|
assertEquals("asmfunc", call.target.nameInSource.single())
|
2021-10-25 21:01:07 +00:00
|
|
|
assertTrue(call.args.single() is IdentifierReference, "str param in function body should not be transformed by normal compiler steps")
|
2021-10-24 18:57:10 +00:00
|
|
|
assertEquals("thing", (call.args.single() as IdentifierReference).nameInSource.single())
|
2021-10-21 22:41:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
2021-10-24 18:57:10 +00:00
|
|
|
fun 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)
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
2021-10-24 18:57:10 +00:00
|
|
|
val result = compileText(C64Target, false, text, writeAssembly = true).assertSuccess()
|
2021-10-29 22:25:34 +00:00
|
|
|
val module = result.program.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"}
|
|
|
|
assertTrue(asmfunc.isAsmSubroutine)
|
|
|
|
assertEquals(DataType.STR, asmfunc.parameters.single().type)
|
2021-10-24 18:57:10 +00:00
|
|
|
assertTrue(asmfunc.statements.single() is Return)
|
2021-10-21 22:41:34 +00:00
|
|
|
assertFalse(func.isAsmSubroutine)
|
2021-10-24 18:57:10 +00:00
|
|
|
assertEquals(DataType.UWORD, func.parameters.single().type, "asmgen should have changed str to uword type")
|
|
|
|
assertTrue(asmfunc.statements.last() is Return)
|
|
|
|
|
2021-10-31 23:24:15 +00:00
|
|
|
assertEquals(5, func.statements.size)
|
|
|
|
assertTrue(func.statements[4] is Return)
|
2021-10-24 18:57:10 +00:00
|
|
|
val paramvar = func.statements[0] as VarDecl
|
|
|
|
assertEquals("thing", paramvar.name)
|
|
|
|
assertEquals(DataType.UWORD, paramvar.datatype, "pre-asmgen should have changed str to uword type")
|
2021-10-31 23:24:15 +00:00
|
|
|
val assign = func.statements[2] as Assignment
|
|
|
|
assertEquals(listOf("t2"), assign.target.identifier!!.nameInSource)
|
|
|
|
assertTrue(assign.value is IdentifierReference, "str param in function body should be treated as plain uword before asmgen")
|
|
|
|
assertEquals("thing", (assign.value as IdentifierReference).nameInSource.single())
|
|
|
|
val call = func.statements[3] as FunctionCallStatement
|
2021-10-24 18:57:10 +00:00
|
|
|
assertEquals("asmfunc", call.target.nameInSource.single())
|
2021-10-25 21:01:07 +00:00
|
|
|
assertTrue(call.args.single() is IdentifierReference, "str param in function body should be treated as plain uword and not been transformed")
|
2021-10-24 18:57:10 +00:00
|
|
|
assertEquals("thing", (call.args.single() as IdentifierReference).nameInSource.single())
|
2021-10-21 22:41:34 +00:00
|
|
|
}
|
|
|
|
|
2021-10-25 21:01:07 +00:00
|
|
|
@Test
|
|
|
|
fun arrayParameterNotYetAllowed_ButShouldPerhapsBe() {
|
|
|
|
// note: the *parser* accepts this as it is valid *syntax*,
|
|
|
|
// however, it's not (yet) valid for the compiler
|
|
|
|
val text = """
|
|
|
|
main {
|
|
|
|
sub start() {
|
|
|
|
}
|
|
|
|
|
|
|
|
asmsub asmfunc(ubyte[] thing @AY) {
|
|
|
|
}
|
|
|
|
|
|
|
|
sub func(ubyte[22] thing) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
|
|
|
|
val errors = ErrorReporterForTests()
|
|
|
|
compileText(C64Target, false, text, errors, false).assertFailure("currently array dt in signature is invalid") // TODO should not be invalid?
|
|
|
|
assertEquals(0, errors.warnings.size)
|
|
|
|
assertContains(errors.errors.single(), ".p8:9:16: Non-string pass-by-reference types cannot occur as a parameter type directly")
|
|
|
|
}
|
|
|
|
|
2021-10-21 22:41:34 +00:00
|
|
|
@Test
|
|
|
|
@Disabled("TODO: allow array parameter in signature") // TODO allow this?
|
|
|
|
fun arrayParameter() {
|
|
|
|
val text = """
|
|
|
|
main {
|
|
|
|
sub start() {
|
|
|
|
ubyte[] array = [1,2,3]
|
|
|
|
|
|
|
|
asmfunc(array)
|
|
|
|
asmfunc([4,5,6])
|
|
|
|
asmfunc($2000)
|
|
|
|
asmfunc(12.345)
|
|
|
|
func(array)
|
|
|
|
func([4,5,6])
|
|
|
|
func($2000)
|
|
|
|
func(12.345)
|
|
|
|
}
|
|
|
|
|
|
|
|
asmsub asmfunc(ubyte[] thing @AY) {
|
|
|
|
}
|
|
|
|
|
|
|
|
sub func(ubyte[22] thing) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
|
2021-10-21 23:25:26 +00:00
|
|
|
val result = compileText(C64Target, false, text, writeAssembly = false).assertSuccess()
|
2021-10-29 22:25:34 +00:00
|
|
|
val module = result.program.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"}
|
|
|
|
assertTrue(asmfunc.isAsmSubroutine)
|
|
|
|
assertEquals(DataType.ARRAY_UB, asmfunc.parameters.single().type)
|
|
|
|
assertTrue(asmfunc.statements.isEmpty())
|
|
|
|
assertFalse(func.isAsmSubroutine)
|
|
|
|
assertEquals(DataType.ARRAY_UB, func.parameters.single().type)
|
|
|
|
assertTrue(func.statements.isEmpty())
|
|
|
|
}
|
2021-11-02 23:47:22 +00:00
|
|
|
|
|
|
|
@Test
|
|
|
|
fun testUwordParameterAndNormalVarIndexedAsArrayWorkAsDirectMemoryRead() {
|
|
|
|
val text="""
|
|
|
|
main {
|
|
|
|
sub thing(uword rr) {
|
|
|
|
ubyte xx = rr[1] ; should still work as var initializer that will be rewritten
|
|
|
|
ubyte yy
|
|
|
|
yy = rr[2]
|
|
|
|
uword other
|
|
|
|
ubyte zz = other[3]
|
|
|
|
}
|
|
|
|
|
|
|
|
sub start() {
|
|
|
|
ubyte[] array=[1,2,3]
|
|
|
|
thing(array)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
|
|
|
|
val result = compileText(C64Target, false, text, writeAssembly = true).assertSuccess()
|
|
|
|
val module = result.program.toplevelModule
|
|
|
|
val block = module.statements.single() as Block
|
|
|
|
val thing = block.statements.filterIsInstance<Subroutine>().single {it.name=="thing"}
|
|
|
|
assertEquals("main", block.name)
|
|
|
|
assertEquals(10, thing.statements.size, "rr, xx, xx assign, yy, yy assign, other, other assign 0, zz, zz assign, return")
|
|
|
|
val xx = thing.statements[1] as VarDecl
|
|
|
|
assertNull(xx.value, "vardecl init values must have been moved to separate assignments")
|
|
|
|
val assignXX = thing.statements[2] as Assignment
|
|
|
|
val assignYY = thing.statements[4] as Assignment
|
|
|
|
val assignZZ = thing.statements[8] as Assignment
|
|
|
|
assertEquals(listOf("xx"), assignXX.target.identifier!!.nameInSource)
|
|
|
|
assertEquals(listOf("yy"), assignYY.target.identifier!!.nameInSource)
|
|
|
|
assertEquals(listOf("zz"), assignZZ.target.identifier!!.nameInSource)
|
|
|
|
val valueXXexpr = (assignXX.value as DirectMemoryRead).addressExpression as BinaryExpression
|
|
|
|
val valueYYexpr = (assignYY.value as DirectMemoryRead).addressExpression as BinaryExpression
|
|
|
|
val valueZZexpr = (assignZZ.value as DirectMemoryRead).addressExpression as BinaryExpression
|
|
|
|
assertEquals(listOf("rr"), (valueXXexpr.left as IdentifierReference).nameInSource)
|
|
|
|
assertEquals(listOf("rr"), (valueYYexpr.left as IdentifierReference).nameInSource)
|
|
|
|
assertEquals(listOf("other"), (valueZZexpr.left as IdentifierReference).nameInSource)
|
|
|
|
assertEquals(1, (valueXXexpr.right as NumericLiteralValue).number.toInt())
|
|
|
|
assertEquals(2, (valueYYexpr.right as NumericLiteralValue).number.toInt())
|
|
|
|
assertEquals(3, (valueZZexpr.right as NumericLiteralValue).number.toInt())
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
fun testUwordParameterAndNormalVarIndexedAsArrayWorkAsMemoryWrite() {
|
|
|
|
val text="""
|
|
|
|
main {
|
|
|
|
sub thing(uword rr) {
|
|
|
|
rr[10] = 42
|
|
|
|
}
|
|
|
|
|
|
|
|
sub start() {
|
|
|
|
ubyte[] array=[1,2,3]
|
|
|
|
thing(array)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
|
|
|
|
val result = compileText(C64Target, false, text, writeAssembly = true).assertSuccess()
|
|
|
|
val module = result.program.toplevelModule
|
|
|
|
val block = module.statements.single() as Block
|
|
|
|
val thing = block.statements.filterIsInstance<Subroutine>().single {it.name=="thing"}
|
|
|
|
assertEquals("main", block.name)
|
|
|
|
assertEquals(3, thing.statements.size, "rr, rr assign, return void")
|
|
|
|
val assignRR = thing.statements[1] as Assignment
|
|
|
|
assertEquals(42, (assignRR.value as NumericLiteralValue).number.toInt())
|
|
|
|
val memwrite = assignRR.target.memoryAddress
|
|
|
|
assertNotNull(memwrite, "memwrite to set byte array value")
|
|
|
|
val addressExpr = memwrite.addressExpression as BinaryExpression
|
|
|
|
assertEquals(listOf("rr"), (addressExpr.left as IdentifierReference).nameInSource)
|
|
|
|
assertEquals("+", addressExpr.operator)
|
|
|
|
assertEquals(10, (addressExpr.right as NumericLiteralValue).number.toInt())
|
|
|
|
}
|
2021-10-21 22:41:34 +00:00
|
|
|
}
|