From 68d2f7d4c07e5b6241abc20905d0d66edccecf91 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 7 Dec 2021 23:21:49 +0100 Subject: [PATCH] allow using ubyte[] as subroutine parameter type (because it is equivalent to uword pointer var) --- .../compiler/astprocessing/AstChecker.kt | 6 +-- .../astprocessing/StatementReorderer.kt | 32 +++++++----- .../compiler/astprocessing/TypecastsAdder.kt | 10 ++-- .../astprocessing/VerifyFunctionArgTypes.kt | 4 +- compiler/test/TestSubroutines.kt | 51 +++++-------------- examples/test.p8 | 14 +---- 6 files changed, 44 insertions(+), 73 deletions(-) diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index 537c8c2a2..a9b1b16e3 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -388,11 +388,11 @@ internal class AstChecker(private val program: Program, 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 + // 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 != 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.") + 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) } } } diff --git a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt index 9cbf22aa7..efce2f317 100644 --- a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt +++ b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt @@ -119,23 +119,28 @@ internal class StatementReorderer(val program: Program, } } + val modifications = mutableListOf() + val subs = subroutine.statements.filterIsInstance() 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() + if(!subroutine.isAsmSubroutine) { val stringParamsByNames = stringParams.associateBy { it.name } - val varsChanges = + varsChanges += if(stringParamsByNames.isNotEmpty()) { subroutine.statements .filterIsInstance() @@ -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 { @@ -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) } diff --git a/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt b/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt index 4342afe77..e02f8690a 100644 --- a/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt +++ b/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt @@ -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 diff --git a/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt b/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt index 4e473ace2..b4f6dad48 100644 --- a/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt +++ b/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt @@ -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 diff --git a/compiler/test/TestSubroutines.kt b/compiler/test/TestSubroutines.kt index 4c1bb72ad..e21a8b6e6 100644 --- a/compiler/test/TestSubroutines.kt +++ b/compiler/test/TestSubroutines.kt @@ -48,10 +48,10 @@ class TestSubroutines: FunSpec({ val asmfunc = mainBlock.statements.filterIsInstance().single { it.name=="asmfunc"} val func = mainBlock.statements.filterIsInstance().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().single { it.name=="asmfunc"} val func = mainBlock.statements.filterIsInstance().single { it.name=="func"} asmfunc.isAsmSubroutine shouldBe true - asmfunc.parameters.single().type shouldBe DataType.STR asmfunc.statements.single() shouldBe instanceOf() 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() @@ -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,10 @@ class TestSubroutines: FunSpec({ val mainBlock = module.statements.single() as Block val asmfunc = mainBlock.statements.filterIsInstance().single { it.name=="asmfunc"} val func = mainBlock.statements.filterIsInstance().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("uword param and normal varindexed as array work as DirectMemoryRead") { diff --git a/examples/test.p8 b/examples/test.p8 index b3d41af31..66a68b172 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -3,17 +3,7 @@ main { sub start() { - - 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() + str name = "irmen" + txt.print(name) } }