mirror of
https://github.com/irmen/prog8.git
synced 2024-11-26 11:49:22 +00:00
allow using ubyte[] as subroutine parameter type (because it is equivalent to uword pointer var)
This commit is contained in:
parent
c812b5ee09
commit
98315de723
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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") {
|
||||
|
@ -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) {
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user