allow using ubyte[] as subroutine parameter type (because it is equivalent to uword pointer var)

This commit is contained in:
Irmen de Jong 2021-12-07 23:21:49 +01:00
parent c812b5ee09
commit 98315de723
6 changed files with 71 additions and 76 deletions

View File

@ -387,13 +387,13 @@ internal class AstChecker(private val program: Program,
if(statusFlagsNoCarry.isNotEmpty())
err("can only use Carry as status flag parameter")
} else {
// Non-string Pass-by-reference datatypes can not occur as parameters to a subroutine directly
// Instead, their reference (address) should be passed (as an UWORD).
for(p in subroutine.parameters) {
if(p.type in PassByReferenceDatatypes && p.type != DataType.STR) {
err("Non-string pass-by-reference types cannot occur as a parameter type directly. Instead, use an uword to receive their address, or access the variable from the outer scope directly.")
}
}
// Non-string and non-ubytearray Pass-by-reference datatypes can not occur as parameters to a subroutine directly
// Instead, their reference (address) should be passed (as an UWORD).
for(p in subroutine.parameters) {
if(p.type in PassByReferenceDatatypes && p.type !in listOf(DataType.STR, DataType.ARRAY_UB)) {
errors.err("this pass-by-reference type can't be used as a parameter type. Instead, use an uword to receive the address, or access the variable from the outer scope directly.", p.position)
}
}
}

View File

@ -119,23 +119,28 @@ internal class StatementReorderer(val program: Program,
}
}
val modifications = mutableListOf<IAstModification>()
val subs = subroutine.statements.filterIsInstance<Subroutine>()
if(subs.isNotEmpty()) {
// all subroutines defined within this subroutine are moved to the end
return subs.map { IAstModification.Remove(it, subroutine) } +
subs.map { IAstModification.InsertLast(it, subroutine) }
// NOTE: this doesn't check if this has already been done!!!
modifications +=
subs.map { IAstModification.Remove(it, subroutine) } +
subs.map { IAstModification.InsertLast(it, subroutine) }
}
if(!subroutine.isAsmSubroutine) {
// change 'str' parameters into 'uword' (just treat it as an address)
val stringParams = subroutine.parameters.filter { it.type==DataType.STR }
val parameterChanges = stringParams.map {
val uwordParam = SubroutineParameter(it.name, DataType.UWORD, it.position)
IAstModification.ReplaceNode(it, uwordParam, subroutine)
}
// change 'str' and 'ubyte[]' parameters into 'uword' (just treat it as an address)
val stringParams = subroutine.parameters.filter { it.type==DataType.STR || it.type==DataType.ARRAY_UB }
val parameterChanges = stringParams.map {
val uwordParam = SubroutineParameter(it.name, DataType.UWORD, it.position)
IAstModification.ReplaceNode(it, uwordParam, subroutine)
}
val varsChanges = mutableListOf<IAstModification>()
if(!subroutine.isAsmSubroutine) {
val stringParamsByNames = stringParams.associateBy { it.name }
val varsChanges =
varsChanges +=
if(stringParamsByNames.isNotEmpty()) {
subroutine.statements
.filterIsInstance<VarDecl>()
@ -146,11 +151,9 @@ internal class StatementReorderer(val program: Program,
}
}
else emptyList()
return parameterChanges + varsChanges
}
return noModifications
return modifications + parameterChanges + varsChanges
}
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
@ -396,7 +399,8 @@ internal class StatementReorderer(val program: Program,
val argDt = argumentValue.inferType(program).getOrElse { throw FatalAstException("invalid dt") }
if(argDt in ArrayDatatypes) {
// pass the address of the array instead
argumentValue = AddressOf(argumentValue as IdentifierReference, argumentValue.position)
if(argumentValue is IdentifierReference)
argumentValue = AddressOf(argumentValue, argumentValue.position)
}
Assignment(AssignTarget(paramIdentifier, null, null, argumentValue.position), argumentValue, argumentValue.position)
}

View File

@ -151,10 +151,12 @@ class TypecastsAdder(val program: Program, val errors: IErrorReporter) : AstWalk
val requiredType = pair.first.type
if (requiredType != argtype) {
if (argtype isAssignableTo requiredType) {
modifications += IAstModification.ReplaceNode(
call.args[index],
TypecastExpression(pair.second, requiredType, true, pair.second.position),
call as Node)
// don't need a cast for pass-by-reference types that are assigned to UWORD
if(requiredType!=DataType.UWORD || argtype !in PassByReferenceDatatypes)
modifications += IAstModification.ReplaceNode(
call.args[index],
TypecastExpression(pair.second, requiredType, true, pair.second.position),
call as Node)
} else if(requiredType == DataType.UWORD && argtype in PassByReferenceDatatypes) {
// We allow STR/ARRAY values in place of UWORD parameters.
// Take their address instead, UNLESS it's a str parameter in the containing subroutine

View File

@ -33,7 +33,9 @@ internal class VerifyFunctionArgTypes(val program: Program) : IAstVisitor {
// there are some exceptions that are considered compatible, such as STR <> UWORD
if(argDt==DataType.STR && paramDt==DataType.UWORD ||
argDt==DataType.UWORD && paramDt==DataType.STR)
argDt==DataType.UWORD && paramDt==DataType.STR ||
argDt==DataType.UWORD && paramDt==DataType.ARRAY_UB ||
argDt==DataType.STR && paramDt==DataType.ARRAY_UB)
return true
return false

View File

@ -48,10 +48,10 @@ class TestSubroutines: FunSpec({
val asmfunc = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="asmfunc"}
val func = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="func"}
asmfunc.isAsmSubroutine shouldBe true
asmfunc.parameters.single().type shouldBe DataType.STR
asmfunc.statements.isEmpty() shouldBe true
func.isAsmSubroutine shouldBe false
withClue("str param for normal subroutine should be changed into UWORD") {
withClue("str param for subroutines should be changed into UWORD") {
asmfunc.parameters.single().type shouldBe DataType.UWORD
func.parameters.single().type shouldBe DataType.UWORD
func.statements.size shouldBe 4
val paramvar = func.statements[0] as VarDecl
@ -100,10 +100,10 @@ class TestSubroutines: FunSpec({
val asmfunc = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="asmfunc"}
val func = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="func"}
asmfunc.isAsmSubroutine shouldBe true
asmfunc.parameters.single().type shouldBe DataType.STR
asmfunc.statements.single() shouldBe instanceOf<Return>()
func.isAsmSubroutine shouldBe false
withClue("asmgen should have changed str to uword type") {
withClue("str param should have been changed to uword") {
asmfunc.parameters.single().type shouldBe DataType.UWORD
func.parameters.single().type shouldBe DataType.UWORD
}
asmfunc.statements.last() shouldBe instanceOf<Return>()
@ -129,49 +129,24 @@ class TestSubroutines: FunSpec({
(call.args.single() as IdentifierReference).nameInSource.single() shouldBe "thing"
}
test("array param not yet allowd (but should perhaps be?)") {
// 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?
errors.warnings.size shouldBe 0
errors.errors.single() shouldContain ".p8:9:17) Non-string pass-by-reference types cannot occur as a parameter type directly"
}
// TODO allow this?
xtest("arrayParameter") {
test("ubyte[] array parameters") {
val text = """
main {
sub start() {
ubyte[] array = [1,2,3]
asmfunc(array)
asmfunc([4,5,6])
asmfunc($2000)
asmfunc(12.345)
asmfunc("zzzz")
func(array)
func([4,5,6])
func($2000)
func(12.345)
func("zzzz")
}
asmsub asmfunc(ubyte[] thing @AY) {
}
sub func(ubyte[22] thing) {
sub func(ubyte[] thing) {
}
}
"""
@ -181,12 +156,31 @@ class TestSubroutines: FunSpec({
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"}
asmfunc.isAsmSubroutine shouldBe true
asmfunc.parameters.single().type shouldBe DataType.ARRAY_UB
asmfunc.statements.isEmpty() shouldBe true
func.isAsmSubroutine shouldBe false
func.parameters.single().type shouldBe DataType.ARRAY_UB
func.statements.isEmpty() shouldBe true
withClue("ubyte array param should have been replaced by UWORD pointer") {
asmfunc.parameters.single().type shouldBe DataType.UWORD
func.parameters.single().type shouldBe DataType.UWORD
}
}
test("not ubyte[] array parameters not allowed") {
val text = """
main {
sub start() {
}
asmsub func1(uword[] thing @AY) {
}
sub func(byte[] thing) {
}
}
"""
val errors = ErrorReporterForTests()
compileText(C64Target, false, text, writeAssembly = false, errors=errors).assertFailure()
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"
}
test("uword param and normal varindexed as array work as DirectMemoryRead") {

View File

@ -3,17 +3,10 @@
main {
sub start() {
str name = "irmen"
txt.print(name)
}
uword array = $8000
array[0] = 10
array[1] = 20
array[2] = 30
txt.print_ub(@($8000))
txt.spc()
txt.print_ub(@($8001))
txt.spc()
txt.print_ub(@($8002))
txt.spc()
sub func(uword[22] thing) {
}
}