fix array initialization

This commit is contained in:
Irmen de Jong 2018-10-11 23:58:47 +02:00
parent 1fe5c943fd
commit cc54e3ec99
4 changed files with 156 additions and 180 deletions

View File

@ -8,32 +8,32 @@
sub start() {
byte[3] barr1 = [-2,-1,2]
byte[3] barr2 = [-22,-1, 3]
ubyte[3] barr3 = [-2,-1,2] ; @ todo error
ubyte[3] barr4 = [1,2,33]
ubyte[3] barr5 = [1,2,-33] ; @todo error
word[3] warr1 = [-2,-1,2]
;word[3] warr2 = [-2,-1,2, 3453] ; @todo ok
uword[3] warr3 = [-2,-1,2]
uword[3] warr4 = [1,2,33.w]
uword[3] warr5 = [1,2,-33] ; @todo error
byte[4] barr1 = -2
byte[4] barr2 = 33
ubyte[4] barr4 = 2
ubyte[4] barr5 = 44
word[4] warr1 = 4444
word[4] warr2 = -5555
word[4] warr2b = -5522
float[4] farr1
float[4] farr2 = 23
float[4] farr3 = 55.636346
byte b1 = 50 * 2
byte b2 = -50 * 2
ubyte ub1 = 50 * 2
word w1 = 999 * 2
word w2 = -999 * 2
uword uw1 = 999 * 2
float f1 = 999*2
float f2 = -999*2
; byte b1 = 50 * 2
; byte b2 = -50 * 2
; ubyte ub1 = 50 * 2
; word w1 = 999 * 2
; word w2 = -999 * 2
; uword uw1 = 999 * 2
; float f1 = 999*2
; float f2 = -999*2
return
}
sub toscreenx(x: float, z: float) -> word {
return floor(x/(4.2+z) * flt(height)) + width // 2
}
; sub toscreenx(x: float, z: float) -> word {
; return floor(x/(4.2+z) * flt(height)) + width // 2
; }
}

View File

@ -560,6 +560,13 @@ class ArraySpec(var x: IExpression, var y: IExpression?, override val position:
y?.linkParents(this)
}
companion object {
fun forArray(v: LiteralValue, heap: HeapValues): ArraySpec {
val arraySize = v.arrayvalue?.size ?: heap.get(v.heapId!!).arraysize
return ArraySpec(LiteralValue.optimalInteger(arraySize, v.position), null, v.position)
}
}
fun process(processor: IAstProcessor) {
x = x.process(processor)
y = y?.process(processor)
@ -1042,7 +1049,12 @@ class LiteralValue(val type: DataType,
val fh = floatvalue?.hashCode() ?: 0x00103456
val sh = strvalue?.hashCode() ?: 0x00014567
val ah = arrayvalue?.hashCode() ?: 0x11119876
return bh xor wh xor fh xor sh xor ah xor type.hashCode()
var hash = bh * 31 xor wh
hash = hash*31 xor fh
hash = hash*31 xor sh
hash = hash*31 xor ah
hash = hash*31 xor type.hashCode()
return hash
}
override fun equals(other: Any?): Boolean {
@ -1906,7 +1918,7 @@ private fun prog8Parser.ExpressionContext.toAst() : IExpression {
litval.charliteral()!=null -> LiteralValue(DataType.UBYTE, bytevalue = Petscii.encodePetscii(litval.charliteral().text.unescape(), true)[0], position = litval.toPosition())
litval.arrayliteral()!=null -> {
val array = litval.arrayliteral()?.toAst()
// byte/word array type difference is not determined here.
// the actual type of the array can not yet be determined here (missing namespace & heap)
// the ConstantFolder takes care of that and converts the type if needed.
LiteralValue(DataType.ARRAY_UB, arrayvalue = array, position = litval.toPosition())
}

View File

@ -415,9 +415,15 @@ class AstChecker(private val namespace: INameScope,
}
when {
decl.value is RangeExpr -> checkValueTypeAndRange(decl.datatype, decl.arrayspec, decl.value as RangeExpr)
decl.value is LiteralValue -> checkValueTypeAndRange(decl.datatype,
decl.arrayspec ?: ArraySpec(LiteralValue.optimalInteger(-2, decl.position), null, decl.position),
decl.value as LiteralValue, heap)
decl.value is LiteralValue -> {
val arraySpec = decl.arrayspec ?: (
if((decl.value as LiteralValue).isArray)
ArraySpec.forArray(decl.value as LiteralValue, heap)
else
ArraySpec(LiteralValue.optimalInteger(-2, decl.position), null, decl.position)
)
checkValueTypeAndRange(decl.datatype, arraySpec, decl.value as LiteralValue, heap)
}
else -> {
err("var/const declaration needs a compile-time constant initializer value, or range, instead found: ${decl.value!!::class.simpleName}")
return super.process(decl)
@ -515,9 +521,12 @@ class AstChecker(private val namespace: INameScope,
if(!compilerOptions.floats && literalValue.type==DataType.FLOAT) {
checkResult.add(SyntaxError("floating point value used, but floating point is not enabled via options", literalValue.position))
}
checkValueTypeAndRange(literalValue.type,
ArraySpec(LiteralValue.optimalInteger(-3, literalValue.position), null, literalValue.position),
literalValue, heap)
val arrayspec =
if(literalValue.isArray)
ArraySpec.forArray(literalValue, heap)
else
ArraySpec(LiteralValue.optimalInteger(-3, literalValue.position), null, literalValue.position)
checkValueTypeAndRange(literalValue.type, arrayspec, literalValue, heap)
val lv = super.process(literalValue)
when(lv.type) {
@ -798,7 +807,7 @@ class AstChecker(private val namespace: INameScope,
}
DataType.ARRAY_UB, DataType.ARRAY_B -> {
// value may be either a single byte, or a byte array (of all constant values)
if(value.type==DataType.ARRAY_UB || value.type==DataType.ARRAY_B) {
if(value.type==targetDt) {
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).array!!.size
val arraySpecSize = arrayspec.size()
if(arraySpecSize!=null && arraySpecSize>0) {
@ -808,66 +817,34 @@ class AstChecker(private val namespace: INameScope,
val expectedSize = constX.asIntegerValue
if (arraySize != expectedSize)
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
}
} else if(value.type==DataType.ARRAY_UW || value.type==DataType.ARRAY_W) {
return err("initialization value must be an array of bytes")
} else {
if(targetDt==DataType.ARRAY_UB) {
val number = value.bytevalue ?: return if (value.floatvalue != null)
err("unsigned byte value expected instead of float; possible loss of precision")
else
err("unsigned byte value expected")
if (number < 0 || number > 255)
return err("value '$number' out of range for unsigned byte")
} else {
val number = value.bytevalue ?: return if (value.floatvalue != null)
err("byte value expected instead of float; possible loss of precision")
else
err("byte value expected")
if (number < -128 || number > 127)
return err("value '$number' out of range for byte")
return true
}
}
return err("invalid array initialization value ${value.type}, expected $targetDt")
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
// value may be either a single word, or a word array
if(value.type==DataType.ARRAY_UB || value.type==DataType.ARRAY_UW || value.type==DataType.ARRAY_B || value.type==DataType.ARRAY_W) {
if(value.type==targetDt) {
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).array!!.size
val arraySpecSize = arrayspec.size()
if(arraySpecSize!=null && arraySpecSize>0) {
// arrayspec is not always known when checking
val constX = arrayspec.x.constValue(namespace, heap)
if(constX?.asIntegerValue==null)
return err("array size specifier must be constant integer value")
val expectedSize = constX.asIntegerValue
if (arraySize != expectedSize)
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
}
} else {
if(targetDt==DataType.ARRAY_UW) {
val number = value.asIntegerValue ?: return if (value.floatvalue != null)
err("unsigned byte or word value expected instead of float; possible loss of precision")
else
err("unsigned byte or word value expected")
if (number < 0 || number > 65535)
return err("value '$number' out of range for unsigned word")
} else {
val number = value.asIntegerValue ?: return if (value.floatvalue != null)
err("byte or word value expected instead of float; possible loss of precision")
else
err("byte or word value expected")
if (number < -32768 || number > 32767)
return err("value '$number' out of range for word")
return true
}
}
return err("invalid array initialization value ${value.type}, expected $targetDt")
}
DataType.ARRAY_F -> {
// value may be either a single float, or a float array
if(value.type==DataType.ARRAY_UB || value.type==DataType.ARRAY_UW || value.type==DataType.ARRAY_F) {
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).arraysize
if(value.type==targetDt) {
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).doubleArray!!.size
val arraySpecSize = arrayspec.size()
if(arraySpecSize!=null && arraySpecSize>0) {
// arrayspec is not always known when checking
val constX = arrayspec.x.constValue(namespace, heap)
if(constX?.asIntegerValue==null)
return err("array size specifier must be constant integer value")
@ -875,17 +852,20 @@ class AstChecker(private val namespace: INameScope,
if (arraySize != expectedSize)
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
}
} else {
val number = value.asNumericValue?.toDouble()
if(number==null)
return err("expected numerical value")
else if (number < FLOAT_MAX_NEGATIVE || number > FLOAT_MAX_POSITIVE)
return err("value '$number' out of range for mfplt5 floating point")
// check if the floating point values are all within range
val doubles = if(value.arrayvalue!=null)
value.arrayvalue.map {it.constValue(namespace, heap)?.asNumericValue!!.toDouble()}.toDoubleArray()
else
heap.get(value.heapId!!).doubleArray!!
if(doubles.any { it < FLOAT_MAX_NEGATIVE || it> FLOAT_MAX_POSITIVE})
return err("floating point value overflow")
return true
}
return err("invalid array initialization value ${value.type}, expected $targetDt")
}
DataType.MATRIX_UB, DataType.MATRIX_B -> {
// value can only be a single byte, or a byte array (which represents the matrix)
if(value.type==DataType.ARRAY_UB || value.type==DataType.ARRAY_B || value.type==DataType.MATRIX_UB || value.type==DataType.MATRIX_B) {
if(value.type==targetDt) {
val arraySpecSize = arrayspec.size()
if(arraySpecSize!=null && arraySpecSize>0) {
val constX = arrayspec.x.constValue(namespace, heap)
@ -897,19 +877,8 @@ class AstChecker(private val namespace: INameScope,
if (matrix.size != expectedSize)
return err("initializer matrix size mismatch (expecting $expectedSize, got ${matrix.size} elements)")
}
} else {
if(targetDt==DataType.MATRIX_UB) {
val number = value.bytevalue
?: return err("unsigned byte value expected")
if (number < 0 || number > 255)
return err("value '$number' out of range for unsigned byte")
} else {
val number = value.bytevalue
?: return err("byte value expected")
if (number < -128 || number > 127)
return err("value '$number' out of range for byte")
}
}
return err("invalid matrix initialization value $value")
}
}
return true

View File

@ -1,6 +1,7 @@
package prog8.optimizing
import prog8.ast.*
import prog8.compiler.CompilerException
import prog8.compiler.HeapValues
import prog8.compiler.target.c64.FLOAT_MAX_NEGATIVE
import prog8.compiler.target.c64.FLOAT_MAX_POSITIVE
@ -54,6 +55,8 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
}
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.MATRIX_UB, DataType.MATRIX_B -> {
val litval = decl.value as? LiteralValue
if(litval?.type==DataType.FLOAT)
errors.add(ExpressionError("array requires only integers here", litval.position))
val size = decl.arrayspec!!.size()
if(litval!=null && litval.isArray) {
// array initializer value is an array already, keep as-is (or convert to WORDs if needed)
@ -67,11 +70,36 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
heap.update(litval.heapId, HeapValues.HeapValue(DataType.ARRAY_UW, null, array.array, null))
decl.value = LiteralValue(decl.datatype, heapId = litval.heapId, position = litval.position)
}
} else if(decl.datatype==DataType.ARRAY_W && litval.type == DataType.ARRAY_B) {
val array = heap.get(litval.heapId)
if(array.array!=null) {
heap.update(litval.heapId, HeapValues.HeapValue(DataType.ARRAY_W, null, array.array, null))
decl.value = LiteralValue(decl.datatype, heapId = litval.heapId, position = litval.position)
}
}
}
} else if (size != null) {
// array initializer is empty or a single int, and we know the size; create the array.
val fillvalue = if (litval == null) 0 else litval.asIntegerValue ?: 0
when(decl.datatype){
DataType.ARRAY_UB, DataType.MATRIX_UB -> {
if(fillvalue !in 0..255)
errors.add(ExpressionError("ubyte value overflow", litval?.position ?: decl.position))
}
DataType.ARRAY_B, DataType.MATRIX_B -> {
if(fillvalue !in -128..127)
errors.add(ExpressionError("byte value overflow", litval?.position ?: decl.position))
}
DataType.ARRAY_UW -> {
if(fillvalue !in 0..65535)
errors.add(ExpressionError("uword value overflow", litval?.position ?: decl.position))
}
DataType.ARRAY_W -> {
if(fillvalue !in -32768..32767)
errors.add(ExpressionError("word value overflow", litval?.position ?: decl.position))
}
else -> {}
}
val fillArray = IntArray(size) { _ -> fillvalue }
val heapId = heap.add(decl.datatype, fillArray)
decl.value = LiteralValue(decl.datatype, heapId = heapId, position = litval?.position ?: decl.position)
@ -93,9 +121,13 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
} else if (size != null) {
// array initializer is empty or a single int, and we know the size; create the array.
val fillvalue = if (litval == null) 0.0 else litval.asNumericValue?.toDouble() ?: 0.0
val fillArray = DoubleArray(size) { _ -> fillvalue }
val heapId = heap.add(decl.datatype, fillArray)
decl.value = LiteralValue(decl.datatype, heapId = heapId, position = litval?.position ?: decl.position)
if(fillvalue< FLOAT_MAX_NEGATIVE || fillvalue> FLOAT_MAX_POSITIVE)
errors.add(ExpressionError("float value overflow", litval?.position ?: decl.position))
else {
val fillArray = DoubleArray(size) { _ -> fillvalue }
val heapId = heap.add(decl.datatype, fillArray)
decl.value = LiteralValue(decl.datatype, heapId = heapId, position = litval?.position ?: decl.position)
}
}
}
else -> return result
@ -437,100 +469,63 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
return super.process(newValue)
}
} else if(literalValue.arrayvalue!=null) {
val newArray = literalValue.arrayvalue.map { it.process(this) }.toTypedArray()
val arrayDt =
if(newArray.any { it.resultingDatatype(namespace, heap) == DataType.FLOAT })
DataType.ARRAY_F
else {
if (newArray.any { it.resultingDatatype(namespace, heap) in setOf(DataType.BYTE, DataType.WORD) }) {
// we have signed values
if (newArray.any { it.resultingDatatype(namespace, heap) == DataType.WORD })
DataType.ARRAY_W
else
DataType.ARRAY_B
} else {
// only unsigned values
if (newArray.any { it.resultingDatatype(namespace, heap) == DataType.UWORD })
DataType.ARRAY_UW
else DataType.ARRAY_UB
}
}
// if the values are all constants, the array is moved to the heap
val allElementsAreConstant = newArray.fold(true) { c, expr-> c and (expr is LiteralValue)}
if(allElementsAreConstant) {
val litArray = newArray.map{ it as? LiteralValue }
if(null in litArray) {
addError(ExpressionError("array/matrix literal can contain only constant values", literalValue.position))
return super.process(literalValue)
}
if(arrayDt==DataType.ARRAY_UB || arrayDt==DataType.MATRIX_UB) {
// all values should be ubytes
val integerArray = litArray.map { (it as LiteralValue).bytevalue }
if (integerArray.any { it == null || it.toInt() !in 0..255 }) {
addError(ExpressionError("byte array elements must all be integers 0..255", literalValue.position))
return super.process(literalValue)
}
val array = integerArray.mapNotNull { it?.toInt() }.toIntArray()
val heapId = heap.add(arrayDt, array)
val newValue = LiteralValue(arrayDt, heapId = heapId, position = literalValue.position)
return super.process(newValue)
} else if(arrayDt==DataType.ARRAY_B || arrayDt==DataType.MATRIX_B) {
// all values should be bytes
val integerArray = litArray.map { (it as LiteralValue).bytevalue }
if(integerArray.any { it==null || it.toInt() !in -128..127 }) {
addError(ExpressionError("byte array elements must all be integers -128..127", literalValue.position))
return super.process(literalValue)
}
val array = integerArray.mapNotNull { it?.toInt() }.toIntArray()
val heapId = heap.add(arrayDt, array)
val newValue = LiteralValue(arrayDt, heapId=heapId, position = literalValue.position)
return super.process(newValue)
} else if(arrayDt==DataType.ARRAY_UW) {
// all values should be bytes or words
val integerArray = litArray.map { (it as LiteralValue).asIntegerValue }
if(integerArray.any {it==null || it !in 0..65535 }) {
addError(ExpressionError("word array elements must all be integers 0..65535", literalValue.position))
return super.process(literalValue)
}
val array = integerArray.filterNotNull().toIntArray()
val heapId = heap.add(arrayDt, array)
val newValue = LiteralValue(DataType.ARRAY_UW, heapId=heapId, position = literalValue.position)
return super.process(newValue)
} else if(arrayDt==DataType.ARRAY_W) {
// all values should be bytes or words
val integerArray = litArray.map { (it as LiteralValue).asIntegerValue }
if(integerArray.any {it==null || it !in -32768..32767 }) {
addError(ExpressionError("word array elements must all be integers -32768..32767", literalValue.position))
return super.process(literalValue)
}
val array = integerArray.filterNotNull().toIntArray()
val heapId = heap.add(arrayDt, array)
val newValue = LiteralValue(DataType.ARRAY_W, heapId=heapId, position = literalValue.position)
return super.process(newValue)
} else if(arrayDt==DataType.ARRAY_F) {
// all values should be bytes, words or floats
val doubleArray = litArray.map { (it as LiteralValue).asNumericValue?.toDouble() }
if(doubleArray.any { it==null || it !in FLOAT_MAX_NEGATIVE..FLOAT_MAX_POSITIVE }) {
addError(ExpressionError("float array elements must all be floats in the acceptable range", literalValue.position))
return super.process(literalValue)
}
val array = doubleArray.filterNotNull().toDoubleArray()
val heapId = heap.add(arrayDt, array)
val newValue = LiteralValue(DataType.ARRAY_F, heapId=heapId, position = literalValue.position)
return super.process(newValue)
}
} else {
addError(ExpressionError("array/matrix literal can contain only constant values", literalValue.position))
}
val newValue = LiteralValue(arrayDt, arrayvalue = newArray, position = literalValue.position)
return super.process(newValue)
return moveArrayToHeap(literalValue)
}
return super.process(literalValue)
}
private fun moveArrayToHeap(arraylit: LiteralValue): LiteralValue {
val array: Array<IExpression> = arraylit.arrayvalue!!.map { it.process(this) }.toTypedArray()
val allElementsAreConstant = array.fold(true) { c, expr-> c and (expr is LiteralValue)}
if(!allElementsAreConstant) {
addError(ExpressionError("array/matrix literal can contain only constant values", arraylit.position))
return arraylit
} else {
val valuesInArray = array.map { it.constValue(namespace, heap)!!.asNumericValue!! }
val integerArray = valuesInArray.map{it.toInt()}.toIntArray()
val doubleArray = valuesInArray.map{it.toDouble()}.toDoubleArray()
val typesInArray: Set<DataType> = array.mapNotNull { it.resultingDatatype(namespace, heap) }.toSet()
val arrayDt =
if(DataType.FLOAT in typesInArray)
DataType.ARRAY_F
else if(DataType.WORD in typesInArray) {
if(DataType.UWORD in typesInArray)
DataType.ARRAY_F
else
DataType.ARRAY_W
} else {
val maxValue = integerArray.max()!!
val minValue = integerArray.min()!!
if (minValue >= 0) {
// unsigned
if (maxValue <= 255)
DataType.ARRAY_UB
else
DataType.ARRAY_UW
} else {
// signed
if (maxValue <= 127)
DataType.ARRAY_B
else
DataType.ARRAY_W
}
}
val heapId = when(arrayDt) {
DataType.ARRAY_UB,
DataType.ARRAY_B,
DataType.ARRAY_UW,
DataType.ARRAY_W,
DataType.MATRIX_UB,
DataType.MATRIX_B -> heap.add(arrayDt, integerArray)
DataType.ARRAY_F -> heap.add(arrayDt, doubleArray)
else -> throw CompilerException("invalid array type")
}
return LiteralValue(arrayDt, heapId = heapId, position = arraylit.position)
}
}
override fun process(arrayIndexedExpression: ArrayIndexedExpression): IExpression {
if(arrayIndexedExpression.array.y!=null) {
if(arrayIndexedExpression.array.size()!=null) {