mirror of
https://github.com/irmen/prog8.git
synced 2024-09-07 19:54:26 +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())
|
if(statusFlagsNoCarry.isNotEmpty())
|
||||||
err("can only use Carry as status flag parameter")
|
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).
|
// Non-string and non-ubytearray Pass-by-reference datatypes can not occur as parameters to a subroutine directly
|
||||||
for(p in subroutine.parameters) {
|
// Instead, their reference (address) should be passed (as an UWORD).
|
||||||
if(p.type in PassByReferenceDatatypes && p.type != DataType.STR) {
|
for(p in subroutine.parameters) {
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,23 +119,28 @@ internal class StatementReorderer(val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val modifications = mutableListOf<IAstModification>()
|
||||||
|
|
||||||
val subs = subroutine.statements.filterIsInstance<Subroutine>()
|
val subs = subroutine.statements.filterIsInstance<Subroutine>()
|
||||||
if(subs.isNotEmpty()) {
|
if(subs.isNotEmpty()) {
|
||||||
// all subroutines defined within this subroutine are moved to the end
|
// all subroutines defined within this subroutine are moved to the end
|
||||||
return subs.map { IAstModification.Remove(it, subroutine) } +
|
// NOTE: this doesn't check if this has already been done!!!
|
||||||
subs.map { IAstModification.InsertLast(it, subroutine) }
|
modifications +=
|
||||||
|
subs.map { IAstModification.Remove(it, subroutine) } +
|
||||||
|
subs.map { IAstModification.InsertLast(it, subroutine) }
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!subroutine.isAsmSubroutine) {
|
// change 'str' and 'ubyte[]' parameters into 'uword' (just treat it as an address)
|
||||||
// change 'str' parameters into 'uword' (just treat it as an address)
|
val stringParams = subroutine.parameters.filter { it.type==DataType.STR || it.type==DataType.ARRAY_UB }
|
||||||
val stringParams = subroutine.parameters.filter { it.type==DataType.STR }
|
val parameterChanges = stringParams.map {
|
||||||
val parameterChanges = stringParams.map {
|
val uwordParam = SubroutineParameter(it.name, DataType.UWORD, it.position)
|
||||||
val uwordParam = SubroutineParameter(it.name, DataType.UWORD, it.position)
|
IAstModification.ReplaceNode(it, uwordParam, subroutine)
|
||||||
IAstModification.ReplaceNode(it, uwordParam, subroutine)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
val varsChanges = mutableListOf<IAstModification>()
|
||||||
|
if(!subroutine.isAsmSubroutine) {
|
||||||
val stringParamsByNames = stringParams.associateBy { it.name }
|
val stringParamsByNames = stringParams.associateBy { it.name }
|
||||||
val varsChanges =
|
varsChanges +=
|
||||||
if(stringParamsByNames.isNotEmpty()) {
|
if(stringParamsByNames.isNotEmpty()) {
|
||||||
subroutine.statements
|
subroutine.statements
|
||||||
.filterIsInstance<VarDecl>()
|
.filterIsInstance<VarDecl>()
|
||||||
@ -146,11 +151,9 @@ internal class StatementReorderer(val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else emptyList()
|
else emptyList()
|
||||||
|
|
||||||
return parameterChanges + varsChanges
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return noModifications
|
return modifications + parameterChanges + varsChanges
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
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") }
|
val argDt = argumentValue.inferType(program).getOrElse { throw FatalAstException("invalid dt") }
|
||||||
if(argDt in ArrayDatatypes) {
|
if(argDt in ArrayDatatypes) {
|
||||||
// pass the address of the array instead
|
// 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)
|
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
|
val requiredType = pair.first.type
|
||||||
if (requiredType != argtype) {
|
if (requiredType != argtype) {
|
||||||
if (argtype isAssignableTo requiredType) {
|
if (argtype isAssignableTo requiredType) {
|
||||||
modifications += IAstModification.ReplaceNode(
|
// don't need a cast for pass-by-reference types that are assigned to UWORD
|
||||||
call.args[index],
|
if(requiredType!=DataType.UWORD || argtype !in PassByReferenceDatatypes)
|
||||||
TypecastExpression(pair.second, requiredType, true, pair.second.position),
|
modifications += IAstModification.ReplaceNode(
|
||||||
call as Node)
|
call.args[index],
|
||||||
|
TypecastExpression(pair.second, requiredType, true, pair.second.position),
|
||||||
|
call as Node)
|
||||||
} else if(requiredType == DataType.UWORD && argtype in PassByReferenceDatatypes) {
|
} else if(requiredType == DataType.UWORD && argtype in PassByReferenceDatatypes) {
|
||||||
// We allow STR/ARRAY values in place of UWORD parameters.
|
// We allow STR/ARRAY values in place of UWORD parameters.
|
||||||
// Take their address instead, UNLESS it's a str parameter in the containing subroutine
|
// 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
|
// there are some exceptions that are considered compatible, such as STR <> UWORD
|
||||||
if(argDt==DataType.STR && paramDt==DataType.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 true
|
||||||
|
|
||||||
return false
|
return false
|
||||||
|
@ -48,10 +48,10 @@ class TestSubroutines: FunSpec({
|
|||||||
val asmfunc = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="asmfunc"}
|
val asmfunc = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="asmfunc"}
|
||||||
val func = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="func"}
|
val func = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="func"}
|
||||||
asmfunc.isAsmSubroutine shouldBe true
|
asmfunc.isAsmSubroutine shouldBe true
|
||||||
asmfunc.parameters.single().type shouldBe DataType.STR
|
|
||||||
asmfunc.statements.isEmpty() shouldBe true
|
asmfunc.statements.isEmpty() shouldBe true
|
||||||
func.isAsmSubroutine shouldBe false
|
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.parameters.single().type shouldBe DataType.UWORD
|
||||||
func.statements.size shouldBe 4
|
func.statements.size shouldBe 4
|
||||||
val paramvar = func.statements[0] as VarDecl
|
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 asmfunc = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="asmfunc"}
|
||||||
val func = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="func"}
|
val func = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="func"}
|
||||||
asmfunc.isAsmSubroutine shouldBe true
|
asmfunc.isAsmSubroutine shouldBe true
|
||||||
asmfunc.parameters.single().type shouldBe DataType.STR
|
|
||||||
asmfunc.statements.single() shouldBe instanceOf<Return>()
|
asmfunc.statements.single() shouldBe instanceOf<Return>()
|
||||||
func.isAsmSubroutine shouldBe false
|
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
|
func.parameters.single().type shouldBe DataType.UWORD
|
||||||
}
|
}
|
||||||
asmfunc.statements.last() shouldBe instanceOf<Return>()
|
asmfunc.statements.last() shouldBe instanceOf<Return>()
|
||||||
@ -129,49 +129,24 @@ class TestSubroutines: FunSpec({
|
|||||||
(call.args.single() as IdentifierReference).nameInSource.single() shouldBe "thing"
|
(call.args.single() as IdentifierReference).nameInSource.single() shouldBe "thing"
|
||||||
}
|
}
|
||||||
|
|
||||||
test("array param not yet allowd (but should perhaps be?)") {
|
test("ubyte[] array parameters") {
|
||||||
// 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") {
|
|
||||||
val text = """
|
val text = """
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
ubyte[] array = [1,2,3]
|
ubyte[] array = [1,2,3]
|
||||||
|
|
||||||
asmfunc(array)
|
asmfunc(array)
|
||||||
asmfunc([4,5,6])
|
|
||||||
asmfunc($2000)
|
asmfunc($2000)
|
||||||
asmfunc(12.345)
|
asmfunc("zzzz")
|
||||||
func(array)
|
func(array)
|
||||||
func([4,5,6])
|
|
||||||
func($2000)
|
func($2000)
|
||||||
func(12.345)
|
func("zzzz")
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub asmfunc(ubyte[] thing @AY) {
|
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 mainBlock = module.statements.single() as Block
|
||||||
val asmfunc = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="asmfunc"}
|
val asmfunc = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="asmfunc"}
|
||||||
val func = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="func"}
|
val func = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="func"}
|
||||||
asmfunc.isAsmSubroutine shouldBe true
|
withClue("ubyte array param should have been replaced by UWORD pointer") {
|
||||||
asmfunc.parameters.single().type shouldBe DataType.ARRAY_UB
|
asmfunc.parameters.single().type shouldBe DataType.UWORD
|
||||||
asmfunc.statements.isEmpty() shouldBe true
|
func.parameters.single().type shouldBe DataType.UWORD
|
||||||
func.isAsmSubroutine shouldBe false
|
}
|
||||||
func.parameters.single().type shouldBe DataType.ARRAY_UB
|
}
|
||||||
func.statements.isEmpty() shouldBe true
|
|
||||||
|
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") {
|
test("uword param and normal varindexed as array work as DirectMemoryRead") {
|
||||||
|
@ -3,17 +3,10 @@
|
|||||||
|
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
|
str name = "irmen"
|
||||||
|
txt.print(name)
|
||||||
|
}
|
||||||
|
|
||||||
uword array = $8000
|
sub func(uword[22] thing) {
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user