From 11de3db25f61c3f628d38a6a0cd270a6813b7005 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sat, 8 Feb 2020 17:38:41 +0100 Subject: [PATCH] simplified heapId for arrayvalues --- .../prog8/ast/expressions/AstExpressions.kt | 15 +--- .../src/prog8/ast/processing/AstChecker.kt | 29 +------ .../ast/processing/AstIdentifiersChecker.kt | 75 ++++++------------- .../VarInitValueAndAddressOfCreator.kt | 3 +- .../src/prog8/ast/statements/AstStatements.kt | 3 - .../src/prog8/optimizer/ConstantFolding.kt | 11 +-- .../src/prog8/optimizer/StatementOptimizer.kt | 6 +- compiler/src/prog8/vm/RuntimeValue.kt | 4 +- compiler/test/LiteralValueTests.kt | 6 +- examples/screencodes.p8 | 4 +- 10 files changed, 42 insertions(+), 114 deletions(-) diff --git a/compiler/src/prog8/ast/expressions/AstExpressions.kt b/compiler/src/prog8/ast/expressions/AstExpressions.kt index 9f2200adf..d89de6043 100644 --- a/compiler/src/prog8/ast/expressions/AstExpressions.kt +++ b/compiler/src/prog8/ast/expressions/AstExpressions.kt @@ -435,12 +435,10 @@ class StringLiteralValue(val value: String, class ArrayLiteralValue(val type: DataType, // only array types val value: Array, - initHeapId: Int? =null, override val position: Position) : Expression() { override lateinit var parent: Node - var heapId = initHeapId - private set + val heapId = ++heapIdSequence override fun linkParents(parent: Node) { this.parent = parent @@ -468,9 +466,9 @@ class ArrayLiteralValue(val type: DataType, // only array types val castArray = value.map{ val num = it as? NumericLiteralValue if(num==null) { - // an array of UWORDs could possibly also contain AddressOfs + // an array of UWORDs could possibly also contain AddressOfs, other stuff can't be casted if (elementType != DataType.UWORD || it !is AddressOf) - throw FatalAstException("weird array element $it") + return null it } else { try { @@ -484,11 +482,6 @@ class ArrayLiteralValue(val type: DataType, // only array types } return null // invalid type conversion from $this to $targettype } - - fun addToHeap() { - if(heapId==null) - heapId = ++heapIdSequence - } } class RangeExpr(var from: Expression, @@ -642,7 +635,7 @@ data class IdentifierReference(val nameInSource: List, override val posi return when (value) { is IdentifierReference -> value.heapId(namespace) is StringLiteralValue -> value.heapId - is ArrayLiteralValue -> value.heapId ?: throw FatalAstException("array is not on the heap: $value") + is ArrayLiteralValue -> value.heapId else -> throw FatalAstException("requires a reference value") } } diff --git a/compiler/src/prog8/ast/processing/AstChecker.kt b/compiler/src/prog8/ast/processing/AstChecker.kt index a8d546086..1b629c23c 100644 --- a/compiler/src/prog8/ast/processing/AstChecker.kt +++ b/compiler/src/prog8/ast/processing/AstChecker.kt @@ -694,9 +694,6 @@ internal class AstChecker(private val program: Program, checkValueTypeAndRangeArray(array.type, null, arrayspec, array) super.visit(array) - - if(array.heapId==null && array.parent !is IFunctionCall) - throw FatalAstException("array should have been moved to heap at ${array.position}") } override fun visit(string: StringLiteralValue) { @@ -824,6 +821,7 @@ internal class AstChecker(private val program: Program, if(targetStatement!=null) checkFunctionCall(targetStatement, functionCallStatement.args, functionCallStatement.position) if(targetStatement is Subroutine && targetStatement.returntypes.isNotEmpty()) { + // TODO add 'void' keyword to make this explicit if(targetStatement.returntypes.size==1) printWarning("result value of subroutine call is discarded", functionCallStatement.position) else @@ -1198,31 +1196,6 @@ internal class AstChecker(private val program: Program, } private fun checkArrayValues(value: ArrayLiteralValue, type: DataType): Boolean { - if (value.heapId == null) { - // hmm weird, array literal that hasn't been moved to the heap yet? - val array = value.value.mapNotNull { it.constValue(program) } - val correct: Boolean - when (type) { - DataType.ARRAY_UB -> { - correct = array.all { it.type == DataType.UBYTE && it.number.toInt() in 0..255 } - } - DataType.ARRAY_B -> { - correct = array.all { it.type == DataType.BYTE && it.number.toInt() in -128..127 } - } - DataType.ARRAY_UW -> { - correct = array.all { it.type == DataType.UWORD && it.number.toInt() in 0..65535 } - } - DataType.ARRAY_W -> { - correct = array.all { it.type == DataType.WORD && it.number.toInt() in -32768..32767 } - } - DataType.ARRAY_F -> correct = true - else -> throw AstException("invalid array type $type") - } - if (!correct) - checkResult.add(ExpressionError("array value out of range for type $type", value.position)) - return correct - } - val array = value.value.map { when (it) { is NumericLiteralValue -> it.number.toInt() diff --git a/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt b/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt index f7567eddf..93637725a 100644 --- a/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt +++ b/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt @@ -229,18 +229,16 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi val array = super.visit(arrayLiteral) if(array is ArrayLiteralValue) { val vardecl = array.parent as? VarDecl - return when { - vardecl!=null -> fixupArrayDatatype(array, vardecl, program) - array.heapId!=null -> { - // fix the datatype of the array (also on the heap) to the 'biggest' datatype in the array - // (we don't know the desired datatype here exactly so we guess) - val datatype = determineArrayDt(array.value) - val litval2 = array.cast(datatype)!! - litval2.parent = array.parent - // finally, replace the literal array by a identifier reference. - makeIdentifierFromRefLv(litval2) - } - else -> array + return if(vardecl!=null) + fixupArrayEltDatatypesFromVardecl(array, vardecl) + else { + // fix the datatype of the array (also on the heap) to the 'biggest' datatype in the array + // (we don't know the desired datatype here exactly so we guess) + val datatype = determineArrayDt(array.value) + val litval2 = array.cast(datatype)!! + litval2.parent = array.parent + // finally, replace the literal array by a identifier reference. + makeIdentifierFromRefLv(litval2) } } return array @@ -279,7 +277,6 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi // a referencetype literal value that's not declared as a variable // we need to introduce an auto-generated variable for this to be able to refer to the value // note: if the var references the same literal value, it is not yet de-duplicated here. - array.addToHeap() val scope = array.definingScope() val variable = VarDecl.createAuto(array) return replaceWithIdentifier(variable, scope, array.parent) @@ -352,7 +349,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi } -internal fun fixupArrayDatatype(array: ArrayLiteralValue, program: Program): ArrayLiteralValue { +internal fun fixupArrayEltDatatypes(array: ArrayLiteralValue, program: Program): ArrayLiteralValue { val dts = array.value.map {it.inferType(program).typeOrElse(DataType.STRUCT)}.toSet() if(dts.any { it !in NumericDatatypes }) { return array @@ -370,50 +367,22 @@ internal fun fixupArrayDatatype(array: ArrayLiteralValue, program: Program): Arr // convert values and array type val elementType = ArrayElementTypes.getValue(dt) val values = array.value.map { (it as NumericLiteralValue).cast(elementType) as Expression}.toTypedArray() - val array2 = ArrayLiteralValue(dt, values, array.heapId, array.position) + val array2 = ArrayLiteralValue(dt, values, array.position) array2.linkParents(array.parent) return array2 } -internal fun fixupArrayDatatype(array: ArrayLiteralValue, vardecl: VarDecl, program: Program): ArrayLiteralValue { - if(array.heapId!=null) { - val arrayDt = array.type - if(arrayDt!=vardecl.datatype) { - // fix the datatype of the array (also on the heap) to match the vardecl - val litval2 = - try { - val result = array.cast(vardecl.datatype) - if(result==null) { - val constElements = array.value.mapNotNull { it.constValue(program) } - val elementDts = constElements.map { it.type } - if(DataType.FLOAT in elementDts) { - array.cast(DataType.ARRAY_F) ?: ArrayLiteralValue(DataType.ARRAY_F, array.value, array.heapId, array.position) - } else { - val numbers = constElements.map { it.number.toInt() } - val minValue = numbers.min()!! - val maxValue = numbers.max()!! - if (minValue >= 0) { - // only positive values, so uword or ubyte - val dt = if(maxValue<256) DataType.ARRAY_UB else DataType.ARRAY_UW - array.cast(dt) ?: ArrayLiteralValue(dt, array.value, array.heapId, array.position) - } else { - // negative value present, so word or byte - val dt = if(minValue >= -128 && maxValue<=127) DataType.ARRAY_B else DataType.ARRAY_W - array.cast(dt) ?: ArrayLiteralValue(dt, array.value, array.heapId, array.position) - } - } - } - else result - } catch(x: ExpressionError) { - // couldn't cast permanently. - // instead, simply adjust the array type and trust the AstChecker to report the exact error - ArrayLiteralValue(vardecl.datatype, array.value, array.heapId, array.position) - } - vardecl.value = litval2 - litval2.linkParents(vardecl) - litval2.addToHeap() - return litval2 +internal fun fixupArrayEltDatatypesFromVardecl(array: ArrayLiteralValue, vardecl: VarDecl): ArrayLiteralValue { + val arrayDt = array.type + if(arrayDt!=vardecl.datatype) { + // fix the datatype of the array (also on the heap) to match the vardecl + val cast = array.cast(vardecl.datatype) + if (cast != null) { + vardecl.value = cast + cast.linkParents(vardecl) + return cast } + // can't be casted yet, attempt again later } return array } diff --git a/compiler/src/prog8/ast/processing/VarInitValueAndAddressOfCreator.kt b/compiler/src/prog8/ast/processing/VarInitValueAndAddressOfCreator.kt index b02e6cbf7..b71b6fb26 100644 --- a/compiler/src/prog8/ast/processing/VarInitValueAndAddressOfCreator.kt +++ b/compiler/src/prog8/ast/processing/VarInitValueAndAddressOfCreator.kt @@ -44,8 +44,7 @@ internal class VarInitValueAndAddressOfCreator(private val program: Program): IA val arraysize = decl.arraysize!!.size()!! val array = ArrayLiteralValue(decl.datatype, Array(arraysize) { NumericLiteralValue.optimalInteger(0, decl.position) }, - null, decl.position) - array.addToHeap() + decl.position) decl.value = array } diff --git a/compiler/src/prog8/ast/statements/AstStatements.kt b/compiler/src/prog8/ast/statements/AstStatements.kt index 48963d4d0..d8272c986 100644 --- a/compiler/src/prog8/ast/statements/AstStatements.kt +++ b/compiler/src/prog8/ast/statements/AstStatements.kt @@ -198,9 +198,6 @@ class VarDecl(val type: VarDeclType, } fun createAuto(array: ArrayLiteralValue): VarDecl { - if(array.heapId==null) - throw FatalAstException("can only create autovar for an array that has a heapid $array") - val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}" val declaredType = ArrayElementTypes.getValue(array.type) val arraysize = ArrayIndex.forArray(array) diff --git a/compiler/src/prog8/optimizer/ConstantFolding.kt b/compiler/src/prog8/optimizer/ConstantFolding.kt index e991bee42..7986ff5a5 100644 --- a/compiler/src/prog8/optimizer/ConstantFolding.kt +++ b/compiler/src/prog8/optimizer/ConstantFolding.kt @@ -5,7 +5,8 @@ import prog8.ast.Program import prog8.ast.base.* import prog8.ast.expressions.* import prog8.ast.processing.IAstModifyingVisitor -import prog8.ast.processing.fixupArrayDatatype +import prog8.ast.processing.fixupArrayEltDatatypesFromVardecl +import prog8.ast.processing.fixupArrayEltDatatypes import prog8.ast.statements.* import prog8.compiler.target.CompilationTarget import prog8.functions.BuiltinFunctions @@ -116,7 +117,6 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor { // create the array itself, filled with the fillvalue. val array = Array(size) {fillvalue}.map { NumericLiteralValue.optimalInteger(it, numericLv.position) as Expression}.toTypedArray() val refValue = ArrayLiteralValue(decl.datatype, array, position = numericLv.position) - refValue.addToHeap() decl.value = refValue refValue.parent=decl optimizationsDone++ @@ -138,7 +138,6 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor { // create the array itself, filled with the fillvalue. val array = Array(size) {fillvalue}.map { NumericLiteralValue(DataType.FLOAT, it, litval.position) as Expression}.toTypedArray() val refValue = ArrayLiteralValue(DataType.ARRAY_F, array, position = litval.position) - refValue.addToHeap() decl.value = refValue refValue.parent=decl optimizationsDone++ @@ -586,7 +585,6 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor { if(array2!=null && array2!==array) { forLoop2.iterable = array2 array2.linkParents(forLoop2) - array2.addToHeap() } } @@ -633,13 +631,12 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor { override fun visit(arrayLiteral: ArrayLiteralValue): Expression { val array = super.visit(arrayLiteral) if(array is ArrayLiteralValue) { - array.addToHeap() val vardecl = array.parent as? VarDecl return if (vardecl!=null) { - fixupArrayDatatype(array, vardecl, program) + fixupArrayEltDatatypesFromVardecl(array, vardecl) } else { // it's not an array associated with a vardecl, attempt to guess the data type from the array values - fixupArrayDatatype(array, program) + fixupArrayEltDatatypes(array, program) } } return array diff --git a/compiler/src/prog8/optimizer/StatementOptimizer.kt b/compiler/src/prog8/optimizer/StatementOptimizer.kt index 3b9f1c6eb..21290ff5d 100644 --- a/compiler/src/prog8/optimizer/StatementOptimizer.kt +++ b/compiler/src/prog8/optimizer/StatementOptimizer.kt @@ -261,7 +261,7 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV if(constvalue!=null) { return if(constvalue.asBooleanValue){ // always true -> keep only if-part - printWarning("condition is always true", ifStatement.position) + printWarning("condition is always true", ifStatement.position) // TODO don't warn this if the condition is just the single value 'true' optimizationsDone++ ifStatement.truepart } else { @@ -310,7 +310,7 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV if(constvalue!=null) { return if(constvalue.asBooleanValue){ // always true -> print a warning, and optimize into body + jump (if there are no continue and break statements) - printWarning("condition is always true", whileLoop.position) + printWarning("condition is always true", whileLoop.position) // TODO don't warn this if the condition is just the single value 'true' if(hasContinueOrBreak(whileLoop.body)) return whileLoop val label = Label("_prog8_back", whileLoop.condition.position) @@ -336,7 +336,7 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV if(constvalue!=null) { return if(constvalue.asBooleanValue){ // always true -> keep only the statement block (if there are no continue and break statements) - printWarning("condition is always true", repeatLoop.position) + printWarning("condition is always true", repeatLoop.position) // TODO don't warn this if the condition is just the single value 'true' if(hasContinueOrBreak(repeatLoop.body)) repeatLoop else { diff --git a/compiler/src/prog8/vm/RuntimeValue.kt b/compiler/src/prog8/vm/RuntimeValue.kt index b7098af23..3f35cd26e 100644 --- a/compiler/src/prog8/vm/RuntimeValue.kt +++ b/compiler/src/prog8/vm/RuntimeValue.kt @@ -605,7 +605,7 @@ open class RuntimeValueArray(type: DataType, val array: Array, val heapI fun fromLv(array: ArrayLiteralValue): RuntimeValueArray { return if (array.type == DataType.ARRAY_F) { val doubleArray = array.value.map { (it as NumericLiteralValue).number }.toTypedArray() - RuntimeValueArray(array.type, doubleArray, array.heapId!!) + RuntimeValueArray(array.type, doubleArray, array.heapId) } else { val resultArray = mutableListOf() for (elt in array.value.withIndex()) { @@ -615,7 +615,7 @@ open class RuntimeValueArray(type: DataType, val array: Array, val heapI resultArray.add((elt.hashCode())) // ...poor man's implementation of ADDRESSOF(array), it probably won't work very well } } - RuntimeValueArray(array.type, resultArray.toTypedArray(), array.heapId!!) + RuntimeValueArray(array.type, resultArray.toTypedArray(), array.heapId) } } } diff --git a/compiler/test/LiteralValueTests.kt b/compiler/test/LiteralValueTests.kt index bf1283e99..1d4df485c 100644 --- a/compiler/test/LiteralValueTests.kt +++ b/compiler/test/LiteralValueTests.kt @@ -93,9 +93,9 @@ class TestParserNumericLiteralValue { val lvTwoR = NumericLiteralValue(DataType.UBYTE, 2, dummyPos) val lvThreeR = NumericLiteralValue(DataType.UBYTE, 3, dummyPos) val lvFour= NumericLiteralValue(DataType.UBYTE, 4, dummyPos) - val lv1 = ArrayLiteralValue(DataType.ARRAY_UB, arrayOf(lvOne, lvTwo, lvThree), null, dummyPos) - val lv2 = ArrayLiteralValue(DataType.ARRAY_UB, arrayOf(lvOneR, lvTwoR, lvThreeR), null, dummyPos) - val lv3 = ArrayLiteralValue(DataType.ARRAY_UB, arrayOf(lvOneR, lvTwoR, lvFour), null, dummyPos) + val lv1 = ArrayLiteralValue(DataType.ARRAY_UB, arrayOf(lvOne, lvTwo, lvThree), dummyPos) + val lv2 = ArrayLiteralValue(DataType.ARRAY_UB, arrayOf(lvOneR, lvTwoR, lvThreeR), dummyPos) + val lv3 = ArrayLiteralValue(DataType.ARRAY_UB, arrayOf(lvOneR, lvTwoR, lvFour), dummyPos) assertEquals(lv1, lv2) assertNotEquals(lv1, lv3) } diff --git a/examples/screencodes.p8 b/examples/screencodes.p8 index 5a7512e85..bb831599a 100644 --- a/examples/screencodes.p8 +++ b/examples/screencodes.p8 @@ -9,8 +9,8 @@ main { c64.VMCSB |= 2 ; switch to lowercase charset - str s1 = "HELLO hello 1234 @[/]\n" - str s2 = c64scr("HELLO hello 1234 @[/]\n") + str s1 = "HELLO hello 1234 @[/]\n" ; regular strings have default encoding (petscii on c64) + str s2 = @"HELLO hello 1234 @[/]\n" ; TODO @-strings for alternate encoding (screencode on c64) c64scr.print("\n\n\n\nString output via print:\n") c64scr.print(s1)