From 17be722e2bc5ac8e338cecd0bb98f5f5248bf9af Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Mon, 15 Jul 2019 23:05:04 +0200 Subject: [PATCH] arrays without init value are once again cleared with zeros --- compiler/src/prog8/ast/antlr/Antr2Kotlin.kt | 15 ++++ .../ast/processing/AstIdentifiersChecker.kt | 5 ++ .../intermediate/IntermediateProgram.kt | 26 ++++-- .../src/prog8/compiler/target/c64/AsmGen.kt | 86 +++++++------------ .../src/prog8/optimizer/ConstantFolding.kt | 2 +- docs/source/programming.rst | 24 +++--- docs/source/syntaxreference.rst | 7 +- examples/test.p8 | 62 +++---------- 8 files changed, 101 insertions(+), 126 deletions(-) diff --git a/compiler/src/prog8/ast/antlr/Antr2Kotlin.kt b/compiler/src/prog8/ast/antlr/Antr2Kotlin.kt index ec903f087..419ec4b4e 100644 --- a/compiler/src/prog8/ast/antlr/Antr2Kotlin.kt +++ b/compiler/src/prog8/ast/antlr/Antr2Kotlin.kt @@ -93,6 +93,21 @@ private fun prog8Parser.StatementContext.toAst() : IStatement { ) } + structvardecl()?.let { + return VarDecl( + VarDeclType.VAR, + DataType.STRUCT, + ZeropageWish.NOT_IN_ZEROPAGE, + null, + it.varname.text, + it.structname.text, + null, + false, + false, + it.toPosition() + ) + } + constdecl()?.let { val cvarinit = it.varinitializer() val vd = cvarinit.vardecl() diff --git a/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt b/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt index dbe9e1b4f..54ca75fa5 100644 --- a/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt +++ b/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt @@ -76,6 +76,11 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi if(decl.struct!!.statements.any { (it as VarDecl).datatype !in NumericDatatypes}) return super.visit(decl) // a non-numeric member, not supported. proper error is given by AstChecker later + if(decl.value is NumericLiteralValue) { + checkResult.add(ExpressionError("you cannot initialize a struct using a single value", decl.position)) + return super.visit(decl) + } + val decls = decl.flattenStructMembers() decls.add(decl) val result = AnonymousScope(decls, decl.position) diff --git a/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt b/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt index e3f2723c4..58ff30afd 100644 --- a/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt +++ b/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt @@ -16,7 +16,7 @@ import java.nio.file.Path class IntermediateProgram(val name: String, var loadAddress: Int, val heap: HeapValues, val source: Path) { - class VariableParameters (val zp: ZeropageWish, val memberOfStruct: StructDecl?, val uninitializedArraySize: Int?) + class VariableParameters (val zp: ZeropageWish, val memberOfStruct: StructDecl?) class Variable(val scopedname: String, val value: RuntimeValue, val params: VariableParameters) class ProgramBlock(val name: String, @@ -403,7 +403,7 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap if(decl.parent is StructDecl) return - val valueparams = VariableParameters(decl.zeropage, decl.struct, null) + val valueparams = VariableParameters(decl.zeropage, decl.struct) val value = when(decl.datatype) { in NumericDatatypes -> { RuntimeValue(decl.datatype, (decl.value as NumericLiteralValue).number) @@ -421,11 +421,21 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap if(litval!=null){ RuntimeValue(decl.datatype, heapId = litval.heapId) } else { - // uninitialized array rather than one filled with zero - val value = RuntimeValue(decl.datatype, heapId=-999) - currentBlock.variables.add(Variable(scopedname, value, - VariableParameters(ZeropageWish.NOT_IN_ZEROPAGE, null, decl.arraysize!!.size()!!))) - return + // uninitialized array. fill it with zeros. + val arraysize = decl.arraysize!!.size()!! + val heapId = + when(decl.datatype){ + DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> { + val array = Array(arraysize) { IntegerOrAddressOf(0, null) } + heap.addIntegerArray(decl.datatype, array) + } + DataType.ARRAY_F -> { + val array = DoubleArray(arraysize) { 0.0 } + heap.addDoublesArray(array) + } + else -> throw CompilerException("weird array dt") + } + RuntimeValue(decl.datatype, heapId=heapId) } } DataType.STRUCT -> { @@ -533,7 +543,7 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap throw CompilerException("zp conflict") val valuestr = variable.value.toString() val struct = if(variable.params.memberOfStruct==null) "" else "struct=${variable.params.memberOfStruct.name}" - out.println("${variable.scopedname} ${variable.value.type.name.toLowerCase()} $valuestr zp=${variable.params.zp} s=$struct u=${variable.params.uninitializedArraySize}") + out.println("${variable.scopedname} ${variable.value.type.name.toLowerCase()} $valuestr zp=${variable.params.zp} s=$struct") } out.println("%end_variables") out.println("%memorypointers") diff --git a/compiler/src/prog8/compiler/target/c64/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/AsmGen.kt index 9eca48831..e99f85f04 100644 --- a/compiler/src/prog8/compiler/target/c64/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/AsmGen.kt @@ -329,75 +329,55 @@ class AsmGen(private val options: CompilationOptions, private val program: Inter } DataType.ARRAY_UB -> { // unsigned integer byte arraysize - if(parameters.uninitializedArraySize!=null) { - out("$varname\t.fill ${parameters.uninitializedArraySize}") // uninitialized array - } else { - val data = makeArrayFillDataUnsigned(value) - if (data.size <= 16) - out("$varname\t.byte ${data.joinToString()}") - else { - out(varname) - for (chunk in data.chunked(16)) - out(" .byte " + chunk.joinToString()) - } + val data = makeArrayFillDataUnsigned(value) + if (data.size <= 16) + out("$varname\t.byte ${data.joinToString()}") + else { + out(varname) + for (chunk in data.chunked(16)) + out(" .byte " + chunk.joinToString()) } } DataType.ARRAY_B -> { // signed integer byte arraysize - if(parameters.uninitializedArraySize!=null) { - out("$varname\t.fill ${parameters.uninitializedArraySize}") // uninitialized array - } else { - val data = makeArrayFillDataSigned(value) - if (data.size <= 16) - out("$varname\t.char ${data.joinToString()}") - else { - out(varname) - for (chunk in data.chunked(16)) - out(" .char " + chunk.joinToString()) - } + val data = makeArrayFillDataSigned(value) + if (data.size <= 16) + out("$varname\t.char ${data.joinToString()}") + else { + out(varname) + for (chunk in data.chunked(16)) + out(" .char " + chunk.joinToString()) } } DataType.ARRAY_UW -> { // unsigned word arraysize - if(parameters.uninitializedArraySize!=null) { - out("$varname\t.fill ${parameters.uninitializedArraySize}*2") // uninitialized array - } else { - val data = makeArrayFillDataUnsigned(value) - if (data.size <= 16) - out("$varname\t.word ${data.joinToString()}") - else { - out(varname) - for (chunk in data.chunked(16)) - out(" .word " + chunk.joinToString()) - } + val data = makeArrayFillDataUnsigned(value) + if (data.size <= 16) + out("$varname\t.word ${data.joinToString()}") + else { + out(varname) + for (chunk in data.chunked(16)) + out(" .word " + chunk.joinToString()) } } DataType.ARRAY_W -> { // signed word arraysize - if(parameters.uninitializedArraySize!=null) { - out("$varname\t.fill ${parameters.uninitializedArraySize}*2") // uninitialized array - } else { - val data = makeArrayFillDataSigned(value) - if (data.size <= 16) - out("$varname\t.sint ${data.joinToString()}") - else { - out(varname) - for (chunk in data.chunked(16)) - out(" .sint " + chunk.joinToString()) - } + val data = makeArrayFillDataSigned(value) + if (data.size <= 16) + out("$varname\t.sint ${data.joinToString()}") + else { + out(varname) + for (chunk in data.chunked(16)) + out(" .sint " + chunk.joinToString()) } } DataType.ARRAY_F -> { // float arraysize - if(parameters.uninitializedArraySize!=null) { - out("$varname\t.fill ${parameters.uninitializedArraySize}*${MachineDefinition.Mflpt5.MemorySize}") // uninitialized array - } else { - val array = heap.get(value.heapId!!).doubleArray!! - val floatFills = array.map { makeFloatFill(MachineDefinition.Mflpt5.fromNumber(it)) } - out(varname) - for (f in array.zip(floatFills)) - out(" .byte ${f.second} ; float ${f.first}") - } + val array = heap.get(value.heapId!!).doubleArray!! + val floatFills = array.map { makeFloatFill(MachineDefinition.Mflpt5.fromNumber(it)) } + out(varname) + for (f in array.zip(floatFills)) + out(" .byte ${f.second} ; float ${f.first}") } DataType.STRUCT -> throw AssemblyError("vars of type STRUCT should have been removed because flattened") } diff --git a/compiler/src/prog8/optimizer/ConstantFolding.kt b/compiler/src/prog8/optimizer/ConstantFolding.kt index 55d9eb785..6da304bd6 100644 --- a/compiler/src/prog8/optimizer/ConstantFolding.kt +++ b/compiler/src/prog8/optimizer/ConstantFolding.kt @@ -140,7 +140,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor { // arraysize initializer is a single int, and we know the size. val fillvalue = litval.number.toDouble() if (fillvalue < FLOAT_MAX_NEGATIVE || fillvalue > FLOAT_MAX_POSITIVE) - errors.add(ExpressionError("float value overflow", litval.position ?: decl.position)) + errors.add(ExpressionError("float value overflow", litval.position)) else { val heapId = program.heap.addDoublesArray(DoubleArray(size) { fillvalue }) decl.value = ReferenceLiteralValue(DataType.ARRAY_F, initHeapId = heapId, position = litval.position) diff --git a/docs/source/programming.rst b/docs/source/programming.rst index 92dab40e8..f5baba806 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -237,6 +237,7 @@ Arrays ^^^^^^ Array types are also supported. They can be made of bytes, words or floats:: + byte[10] array ; array of 10 bytes, initially set to 0 byte[] array = [1, 2, 3, 4] ; initialize the array, size taken from value byte[99] array = 255 ; initialize array with 99 times 255 [255, 255, 255, 255, ...] byte[] array = 100 to 199 ; initialize array with [100, 101, ..., 198, 199] @@ -285,9 +286,9 @@ Structs are a bit limited in Prog8: you can only use numerical variables as member of a struct, so strings and arrays and other structs can not be part of a struct. Also, it is not possible to use a struct itself inside an array. Structs are mainly syntactic sugar for repeated groups of vardecls -and assignments that belong together. However, *they are layed out -in sequence in memory as the members are defined* which may be useful -if you want to pass pointers around +and assignments that belong together. However, +*they are layed out in sequence in memory as the members are defined* +which may be usefulif you want to pass pointers around. To create a variable of a struct type you need to define the struct itself, and then create a variable with it:: @@ -298,7 +299,7 @@ and then create a variable with it:: ubyte blue } - Color rgb = [255,122,0] + Color rgb = {255,122,0} ; note the curly braces here instead of brackets Color another ; the init value is optional, like arrays another = rgb ; assign all of the values of rgb to another @@ -354,10 +355,17 @@ Initial values across multiple runs of the program When declaring values with an initial value, this value will be set into the variable each time the program reaches the declaration again. This can be in loops, multiple subroutine calls, or even multiple invocations of the entire program. If you omit an initial value, it will -be set to zero only for the first run of the program. A second run will utilize the last value +be set to zero *but only for the first run of the program*. A second run will utilize the last value where it left off (but your code will be a bit smaller because no initialization instructions are generated) +This only works for simple types, *and not for string variables and arrays*. +It is assumed these are left unchanged by the program; they are not re-initialized on +a second run. +If you do modify them in-place, you should take care yourself that they work as +expected when the program is restarted. +(This is an optimization choice to avoid having to store two copies of every string and array) + .. caution:: variables that get allocated in zero-page will *not* have a zero starting value when you omit the variable's initialization. They'll be whatever the last value in that zero page @@ -367,12 +375,6 @@ are generated) this behavior may change in a future version so that subsequent runs always use the same initial values -This only works for simple types, *and not for string variables and arrays*. -It is assumed these are left unchanged by the program. -If you do modify them in-place, you should take care yourself that they work as -expected when the program is restarted. -(This is an optimization choice to avoid having to store two copies of every string and array) - Loops ----- diff --git a/docs/source/syntaxreference.rst b/docs/source/syntaxreference.rst index 80466d854..a486346ee 100644 --- a/docs/source/syntaxreference.rst +++ b/docs/source/syntaxreference.rst @@ -231,10 +231,11 @@ Various examples:: str name = "my name is Irmen" uword address = &counter byte[] values = [11, 22, 33, 44, 55] + byte[5] values ; array of 5 bytes, initially set to zero byte[5] values = 255 ; initialize with five 255 bytes word @zp zpword = 9999 ; prioritize this when selecting vars for zeropage storage - Color rgb = [1,255,0] ; a struct variable + Color rgb = {1,255,0} ; a struct variable with initial values Data types @@ -374,6 +375,10 @@ and other structs can not be part of a struct. Vice versa, a struct can not occu After defining a struct you can use the name of the struct as a data type to declare variables with. +Struct variables can be assigned a struct literal value (also in their declaration as initial value):: + + Color rgb = {255, 100, 0} ; curly braces instead of brackets + Operators --------- diff --git a/examples/test.p8 b/examples/test.p8 index d33705af7..19dbcbc51 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -5,60 +5,18 @@ ~ main { + struct Color { + word red + byte green + float blue + } + sub start() { - byte[] array=[1,2,3,4,5] - word[5] warray - float[5] flarray + Color rgb1 + Color rgb2 - warray[0]=flarray[0] as word - - ubyte length = len(array) - c64scr.print_ub(length) - c64.CHROUT(',') - ubyte length1 = any(array) - c64scr.print_ub(length1) - c64.CHROUT(',') - ubyte length1b = all(array) - c64scr.print_ub(length1b) - c64.CHROUT(',') - ubyte length1c = max(array) - c64scr.print_ub(length1c) - c64.CHROUT(',') - ubyte length1d = min(array) - c64scr.print_ub(length1d) - c64.CHROUT(',') - ubyte xlength = len([1,2,3]) - c64scr.print_ub(xlength) - c64.CHROUT('\n') - ubyte xlength1 = any([1,0,3]) - c64scr.print_ub(xlength1) - c64.CHROUT(',') - ubyte xlength1b = all([1,0,3]) - c64scr.print_ub(xlength1b) - c64.CHROUT(',') - ubyte xlength1c = max([1,2,3]) - c64scr.print_ub(xlength1c) - c64.CHROUT(',') - ubyte xlength1d = min([1,2,3]) - c64scr.print_ub(xlength1d) - c64.CHROUT('\n') - - word s1 = sum(array) - c64scr.print_w(s1) - c64.CHROUT(',') - - uword s2 = sum([1,23]) - c64scr.print_uw(s2) - c64.CHROUT(',') - - float ff1=avg(array) - c64flt.print_f(ff1) - c64.CHROUT(',') - float ff2=avg([1,2,3]) - - c64flt.print_f(ff2) - c64.CHROUT('\n') - return + c64scr.print_b(rgb1.green) + c64scr.print_b(rgb2.green) } }