fix array value initialization type conversions

This commit is contained in:
Irmen de Jong 2018-12-19 00:57:57 +01:00
parent 3a7b341f47
commit 00ad285d3f
3 changed files with 132 additions and 40 deletions

View File

@ -1,5 +1,6 @@
package prog8.ast
import com.sun.org.apache.xpath.internal.operations.Bool
import prog8.compiler.CompilationOptions
import prog8.compiler.HeapValues
import prog8.compiler.target.c64.FLOAT_MAX_NEGATIVE
@ -906,6 +907,8 @@ class AstChecker(private val namespace: INameScope,
DataType.ARRAY_UB, DataType.ARRAY_B -> {
// value may be either a single byte, or a byte arrayspec (of all constant values)
if(value.type==targetDt) {
if(!checkArrayValues(value, targetDt))
return false
val arraySpecSize = arrayspec.size()
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).arraysize
if(arraySpecSize!=null && arraySpecSize>0) {
@ -926,6 +929,8 @@ class AstChecker(private val namespace: INameScope,
DataType.ARRAY_UW, DataType.ARRAY_W -> {
// value may be either a single word, or a word arrayspec
if(value.type==targetDt) {
if(!checkArrayValues(value, targetDt))
return false
val arraySpecSize = arrayspec.size()
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).arraysize
if(arraySpecSize!=null && arraySpecSize>0) {
@ -946,6 +951,8 @@ class AstChecker(private val namespace: INameScope,
DataType.ARRAY_F -> {
// value may be either a single float, or a float arrayspec
if(value.type==targetDt) {
if(!checkArrayValues(value, targetDt))
return false
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).doubleArray!!.size
val arraySpecSize = arrayspec.size()
if(arraySpecSize!=null && arraySpecSize>0) {
@ -975,6 +982,30 @@ class AstChecker(private val namespace: INameScope,
return true
}
private fun checkArrayValues(value: LiteralValue, type: DataType): Boolean {
val array = heap.get(value.heapId!!)
val correct: Boolean
when(type) {
DataType.ARRAY_UB -> {
correct=array.array!=null && array.array.all { it in 0..255 }
}
DataType.ARRAY_B -> {
correct=array.array!=null && array.array.all { it in -128..127 }
}
DataType.ARRAY_UW -> {
correct=array.array!=null && array.array.all { it in 0..65535 }
}
DataType.ARRAY_W -> {
correct=array.array!=null && array.array.all { it in -32768..32767 }
}
DataType.ARRAY_F -> correct = array.doubleArray!=null
else -> throw AstException("invalid array type $type")
}
if(!correct)
checkResult.add(ExpressionError("array value out of range for type $type", value.position))
return correct
}
private fun checkAssignmentCompatible(targetDatatype: DataType,
sourceDatatype: DataType,
sourceValue: IExpression,

View File

@ -32,38 +32,22 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
val result = super.process(decl)
if(decl.type==VarDeclType.CONST || decl.type==VarDeclType.VAR) {
val litval = decl.value as? LiteralValue
if(litval!=null && litval.isArray)
fixupArrayTypeOnHeap(decl, litval)
when(decl.datatype) {
DataType.FLOAT -> {
// vardecl: for float vars, promote constant integer initialization values to floats
val literal = decl.value as? LiteralValue
if (literal != null && literal.type in IntegerDatatypes) {
val newValue = LiteralValue(DataType.FLOAT, floatvalue = literal.asNumericValue!!.toDouble(), position = literal.position)
// vardecl: for scalar float vars, promote constant integer initialization values to floats
if (litval != null && litval.type in IntegerDatatypes) {
val newValue = LiteralValue(DataType.FLOAT, floatvalue = litval.asNumericValue!!.toDouble(), position = litval.position)
decl.value = newValue
}
}
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
val litval = decl.value as? LiteralValue
if(litval?.type==DataType.FLOAT)
errors.add(ExpressionError("arrayspec requires only integers here", litval.position))
val size = decl.arrayspec!!.size()
if(litval!=null && litval.isArray) {
// arrayspec initializer value is an arrayspec already, keep as-is (or convert to WORDs if needed)
if(litval.heapId!=null) {
if(decl.datatype==DataType.ARRAY_UW && litval.type == DataType.ARRAY_UB) {
val array = heap.get(litval.heapId)
if(array.array!=null) {
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) {
if ((litval==null || !litval.isArray) && size != null) {
// arrayspec initializer is empty or a single int, and we know the size; create the arrayspec.
val fillvalue = if (litval == null) 0 else litval.asIntegerValue ?: 0
when(decl.datatype){
@ -91,19 +75,8 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
}
}
DataType.ARRAY_F -> {
val litval = decl.value as? LiteralValue
val size = decl.arrayspec!!.size()
if(litval!=null && litval.isArray) {
// arrayspec initializer value is an arrayspec already, make sure to convert to floats
if(litval.heapId!=null) {
val array = heap.get(litval.heapId)
if (array.doubleArray == null) {
val doubleArray = array.array!!.map { it.toDouble() }.toDoubleArray()
heap.update(litval.heapId, HeapValues.HeapValue(DataType.ARRAY_F, null, null, doubleArray))
decl.value = LiteralValue(decl.datatype, heapId = litval.heapId, position = litval.position)
}
}
} else if (size != null) {
if ((litval==null || !litval.isArray) && size != null) {
// arrayspec initializer is empty or a single int, and we know the size; create the arrayspec.
val fillvalue = if (litval == null) 0.0 else litval.asNumericValue?.toDouble() ?: 0.0
if(fillvalue< FLOAT_MAX_NEGATIVE || fillvalue> FLOAT_MAX_POSITIVE)
@ -121,6 +94,32 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
return result
}
private fun fixupArrayTypeOnHeap(decl: VarDecl, litval: LiteralValue) {
// fix the type of the array value that's on the heap, to match the vardecl.
// notice that checking the bounds of the actual values is not done here, but in the AstChecker later.
if(decl.datatype==litval.type)
return // already correct datatype
val heapId = litval.heapId!!
val array=heap.get(heapId)
when(decl.datatype) {
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
if(array.array!=null) {
heap.update(heapId, HeapValues.HeapValue(decl.datatype, null, array.array, null))
decl.value = LiteralValue(decl.datatype, heapId=heapId, position = litval.position)
}
}
DataType.ARRAY_F -> {
if(array.array!=null) {
// convert a non-float array to floats
val doubleArray = array.array.map { it.toDouble() }.toDoubleArray()
heap.update(heapId, HeapValues.HeapValue(DataType.ARRAY_F, null, null, doubleArray))
decl.value = LiteralValue(decl.datatype, heapId = heapId, position = litval.position)
}
}
else -> throw AstException("invalid array vardecl type")
}
}
/**
* replace identifiers that refer to const value, with the value itself (if it's a simple type)
*/
@ -484,14 +483,15 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
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()
// Take an educated guess about the array type.
// This may be altered (if needed & if possible) to suit an array declaration type later!
// Also, the check if all values are valid for the given datatype is done later, in the AstChecker.
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
DataType.ARRAY_W
} else {
val maxValue = integerArray.max()!!
val minValue = integerArray.min()!!

View File

@ -290,7 +290,7 @@ dec_var_f .proc
.pend
pop_2_floats_f2_in_fac1 .proc
; -- pop 2 floats from stack, load the second one in FAC1
; -- pop 2 floats from stack, load the second one in FAC1 as well
lda #<fmath_float2
ldy #>fmath_float2
jsr pop_float
@ -1100,6 +1100,67 @@ _cmp_mod cpy #255 ; modified
rts
.pend
func_max_ub .proc
inx
rts
.warn "todo func_max_ub"
.pend
func_max_b .proc
inx
rts
.warn "todo func_max_b"
.pend
func_max_uw .proc
inx
rts
.warn "todo func_max_uw"
.pend
func_max_w .proc
inx
rts
.warn "todo func_max_w"
.pend
func_max_f .proc
inx
rts
.warn "todo func_max_f"
.pend
func_min_ub .proc
inx
rts
.warn "todo func_min_ub"
.pend
func_min_b .proc
inx
rts
.warn "todo func_min_b"
.pend
func_min_uw .proc
inx
rts
.warn "todo func_min_uw"
.pend
func_min_w .proc
inx
rts
.warn "todo func_min_w"
.pend
func_min_f .proc
inx
rts
.warn "todo func_min_f"
.pend
func_len_str .proc
; -- push length of 0-terminated string on stack
jsr peek_address