implemented floating-point arrays

This commit is contained in:
Irmen de Jong 2018-10-02 01:52:08 +02:00
parent 0cdae48ce7
commit 38e7d48492
13 changed files with 279 additions and 136 deletions

View File

@ -4,18 +4,54 @@
sub start() {
byte[100] array
byte[4,5] mvar
byte[10] barray1
byte[10] barray2 = 11
byte[10] barray3 = [1,2,3,4,5,6,7,8,9,255]
A=AX[2]
A=AY[2]
A=XY[2]
word[10] warray1
word[10] warray2 = 112233
word[10] warray3 = [1,2,3,4,5,6,7,8,9, 65535]
byte[4,5] mvar1
byte[4,5] mvar2 = 22
byte[2,3] mvar3 = [1,2,3,4,5,6]
float[3] farray1
float[3] farray2 = 33.44
float[3] farray2b = 33
float[3] farray3 = [1,2,3]
float[3] farray4 = [1,2,35566]
float[3] farray5 = [1,2.22334,3.1415]
byte i
word w
for i in 0 to 2 {
_vm_write_num(farray5[i])
_vm_write_char('\n')
}
for w in [1,2,3777] { ;@todo loop over array literal
_vm_write_num(w)
_vm_write_char('\n')
}
for i in barray3 { ; @todo loop over symbol
_vm_write_num(i)
_vm_write_char('\n')
}
for i in "hello" { ; @todo loop over string
_vm_write_num(i)
_vm_write_char('\n')
}
for w in "hello" { ; @todo loop over string
_vm_write_num(w)
_vm_write_char('\n')
}
A=array[98]
A=array[99]
A=mvar[13]
A=mvar[2,3]
A=mvar[Y,X]
return
}

View File

@ -3,6 +3,7 @@ package prog8.ast
import org.antlr.v4.runtime.ParserRuleContext
import org.antlr.v4.runtime.tree.TerminalNode
import prog8.compiler.HeapValues
import prog8.compiler.target.c64.Mflpt5
import prog8.compiler.target.c64.Petscii
import prog8.compiler.unescape
import prog8.functions.BuiltinFunctions
@ -26,6 +27,7 @@ enum class DataType {
STR_PS,
ARRAY,
ARRAY_W,
ARRAY_F,
MATRIX
}
@ -60,7 +62,9 @@ enum class BranchCondition {
POS
}
val IterableDatatypes = setOf(DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS, DataType.ARRAY, DataType.ARRAY_W, DataType.MATRIX)
val IterableDatatypes = setOf(
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS,
DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.MATRIX)
class FatalAstException (override var message: String) : Exception(message)
@ -599,8 +603,9 @@ class VarDecl(val type: VarDeclType,
else -> when (declaredDatatype) {
DataType.BYTE -> DataType.ARRAY
DataType.WORD -> DataType.ARRAY_W
DataType.FLOAT -> DataType.ARRAY_F
else -> {
datatypeErrors.add(SyntaxError("array can only contain bytes or words", position))
datatypeErrors.add(SyntaxError("array can only contain bytes/words/floats", position))
DataType.BYTE
}
}
@ -620,7 +625,7 @@ class VarDecl(val type: VarDeclType,
get() = when(datatype) {
DataType.BYTE -> 1
DataType.WORD -> 2
DataType.FLOAT -> 5 // MFLPT5
DataType.FLOAT -> Mflpt5.MemorySize
DataType.STR,
DataType.STR_P,
DataType.STR_S,
@ -636,6 +641,10 @@ class VarDecl(val type: VarDeclType,
val aX = arrayspec?.x as? LiteralValue ?: throw ExpressionError("need constant value expression for arrayspec", position)
2*aX.asIntegerValue!!
}
DataType.ARRAY_F -> {
val aX = arrayspec?.x as? LiteralValue ?: throw ExpressionError("need constant value expression for arrayspec", position)
Mflpt5.MemorySize*aX.asIntegerValue!!
}
DataType.MATRIX -> {
val aX = arrayspec?.x as? LiteralValue ?: throw ExpressionError("need constant value expression for arrayspec", position)
val aY = arrayspec.y as? LiteralValue ?: throw ExpressionError("need constant value expression for arrayspec", position)
@ -833,10 +842,11 @@ class ArrayIndexedExpression(val identifier: IdentifierReference?,
val target = identifier?.targetStatement(namespace)
if (target is VarDecl) {
return when (target.datatype) {
DataType.BYTE, DataType.WORD, DataType.FLOAT -> null
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> DataType.BYTE
DataType.ARRAY, DataType.MATRIX -> DataType.BYTE
DataType.ARRAY_W -> DataType.WORD
else -> null
DataType.ARRAY_F -> DataType.FLOAT
}
}
throw FatalAstException("cannot get indexed element on $target")
@ -861,7 +871,7 @@ class LiteralValue(val type: DataType,
val isString = type==DataType.STR || type==DataType.STR_P || type==DataType.STR_S || type==DataType.STR_PS
val isNumeric = type==DataType.BYTE || type==DataType.WORD || type==DataType.FLOAT
val isArray = type==DataType.ARRAY || type==DataType.ARRAY_W || type==DataType.MATRIX
val isArray = type==DataType.ARRAY || type==DataType.ARRAY_W || type==DataType.ARRAY_F || type==DataType.MATRIX
companion object {
fun fromBoolean(bool: Boolean, position: Position) =
@ -908,7 +918,7 @@ class LiteralValue(val type: DataType,
DataType.FLOAT -> if(floatvalue==null) throw FatalAstException("literal value missing floatvalue")
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS ->
if(strvalue==null && heapId==null) throw FatalAstException("literal value missing strvalue/heapId")
DataType.ARRAY, DataType.ARRAY_W, DataType.MATRIX ->
DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.MATRIX ->
if(arrayvalue==null && heapId==null) throw FatalAstException("literal value missing arrayvalue/heapId")
}
if(bytevalue==null && wordvalue==null && floatvalue==null && arrayvalue==null && strvalue==null && heapId==null)
@ -952,7 +962,7 @@ class LiteralValue(val type: DataType,
if(heapId!=null) "str:#$heapId"
else "str:$strvalue"
}
DataType.ARRAY, DataType.ARRAY_W -> {
DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F -> {
if(heapId!=null) "array:#$heapId"
else "array:$arrayvalue"
}

View File

@ -2,6 +2,8 @@ package prog8.ast
import prog8.compiler.CompilationOptions
import prog8.compiler.HeapValues
import prog8.compiler.target.c64.FLOAT_MAX_NEGATIVE
import prog8.compiler.target.c64.FLOAT_MAX_POSITIVE
import prog8.functions.BuiltinFunctions
import prog8.parser.ParsingFailedError
@ -111,7 +113,7 @@ class AstChecker(private val namespace: INameScope,
if (iterableDt != DataType.WORD && iterableDt != DataType.BYTE &&
iterableDt != DataType.STR && iterableDt != DataType.STR_P &&
iterableDt != DataType.STR_S && iterableDt != DataType.STR_PS &&
iterableDt !=DataType.ARRAY && iterableDt!=DataType.ARRAY_W && iterableDt!=DataType.MATRIX)
iterableDt !=DataType.ARRAY && iterableDt!=DataType.ARRAY_W && iterableDt!=DataType.ARRAY_F && iterableDt!=DataType.MATRIX)
checkResult.add(ExpressionError("register pair can only loop over words", forLoop.position))
}
}
@ -127,13 +129,16 @@ class AstChecker(private val namespace: INameScope,
if(iterableDt!=DataType.BYTE && iterableDt!=DataType.ARRAY && iterableDt!=DataType.MATRIX &&
iterableDt != DataType.STR && iterableDt != DataType.STR_P &&
iterableDt != DataType.STR_S && iterableDt != DataType.STR_PS)
checkResult.add(ExpressionError("can only loop over bytes", forLoop.position))
checkResult.add(ExpressionError("byte loop variable can only loop over bytes", forLoop.position))
}
DataType.WORD -> {
if(iterableDt!=DataType.BYTE && iterableDt!=DataType.WORD &&
iterableDt !=DataType.ARRAY && iterableDt!=DataType.ARRAY_W && iterableDt!=DataType.MATRIX)
checkResult.add(ExpressionError("can only loop over bytes or words", forLoop.position))
iterableDt !=DataType.ARRAY && iterableDt!=DataType.ARRAY_W && iterableDt!=DataType.MATRIX &&
iterableDt != DataType.STR && iterableDt != DataType.STR_P &&
iterableDt != DataType.STR_S && iterableDt != DataType.STR_PS)
checkResult.add(ExpressionError("word loop variable can only loop over bytes or words", forLoop.position))
}
// there's no support for a floating-point loop variable
else -> checkResult.add(ExpressionError("loop variable must be byte or word type", forLoop.position))
}
}
@ -331,7 +336,7 @@ class AstChecker(private val namespace: INameScope,
litVal.parent = decl
decl.value = litVal
}
else -> err("var/const declaration needs a compile-time constant initializer value for this type")
else -> err("var/const declaration needs a compile-time constant initializer value for this type") // const fold should have provided it!
}
return super.process(decl)
}
@ -438,11 +443,11 @@ class AstChecker(private val namespace: INameScope,
if(lv.heapId==null)
throw FatalAstException("string should have been moved to heap at ${lv.position}")
}
DataType.ARRAY, DataType.ARRAY_W, DataType.MATRIX -> {
DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.MATRIX -> {
if(lv.heapId==null)
throw FatalAstException("array/matrix should have been moved to heap at ${lv.position}")
}
else -> {}
DataType.BYTE, DataType.WORD, DataType.FLOAT -> {}
}
return lv
}
@ -626,7 +631,7 @@ class AstChecker(private val namespace: INameScope,
}
return true
}
DataType.ARRAY, DataType.ARRAY_W, DataType.MATRIX -> {
DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.MATRIX -> {
// range and length check bytes
val expectedSize = arrayspec!!.size()
val rangeSize=range.size()
@ -721,17 +726,39 @@ class AstChecker(private val namespace: INameScope,
return err("value '$number' out of range for unsigned word")
}
}
DataType.ARRAY_F -> {
// value may be either a single float, or a float array
if(value.type==DataType.ARRAY || value.type==DataType.ARRAY_W || value.type==DataType.ARRAY_F) {
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).doubleArray!!.size
if(arrayspec!=null) {
// 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 {
val number = value.asNumericValue!!.toDouble()
if (number < FLOAT_MAX_NEGATIVE || number > FLOAT_MAX_POSITIVE)
return err("value '$number' out of range for mfplt5 floating point")
}
}
DataType.MATRIX -> {
// value can only be a single byte, or a byte array (which represents the matrix)
if(value.type==DataType.ARRAY || value.type==DataType.MATRIX) {
val constX = arrayspec!!.x.constValue(namespace, heap)
val constY = arrayspec.y!!.constValue(namespace, heap)
if(constX?.asIntegerValue==null || constY?.asIntegerValue==null)
return err("matrix size specifiers must be constant integer values")
val matrix = heap.get(value.heapId!!).array!!
val expectedSize = constX.asIntegerValue * constY.asIntegerValue
if (matrix.size != expectedSize)
return err("initializer matrix size mismatch (expecting $expectedSize, got ${matrix.size} elements)")
if(arrayspec!=null) {
// arrayspec is not always known when checking
val constX = arrayspec.x.constValue(namespace, heap)
val constY = arrayspec.y!!.constValue(namespace, heap)
if (constX?.asIntegerValue == null || constY?.asIntegerValue == null)
return err("matrix size specifiers must be constant integer values")
val matrix = heap.get(value.heapId!!).array!!
val expectedSize = constX.asIntegerValue * constY.asIntegerValue
if (matrix.size != expectedSize)
return err("initializer matrix size mismatch (expecting $expectedSize, got ${matrix.size} elements)")
}
} else {
val number = value.bytevalue
?: return err("unsigned byte value expected")

View File

@ -51,18 +51,19 @@ fun String.unescape(): String {
class HeapValues {
data class HeapValue(val type: DataType, val str: String?, val array: IntArray?) {
data class HeapValue(val type: DataType, val str: String?, val array: IntArray?, val doubleArray: DoubleArray?) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as HeapValue
return type==other.type && str==other.str && Arrays.equals(array, other.array)
return type==other.type && str==other.str && Arrays.equals(array, other.array) && Arrays.equals(doubleArray, other.doubleArray)
}
override fun hashCode(): Int {
var result = type.hashCode()
result = 31 * result + (str?.hashCode() ?: 0)
result = 31 * result + (array?.let { Arrays.hashCode(it) } ?: 0)
result = 31 * result + (doubleArray?.let { Arrays.hashCode(it) } ?: 0)
return result
}
}
@ -74,7 +75,7 @@ class HeapValues {
throw IllegalArgumentException("string length must be 1-255")
// strings are 'interned' and shared if they're the same
val value = HeapValue(type, str, null)
val value = HeapValue(type, str, null, null)
val existing = heap.indexOf(value)
if(existing>=0)
return existing
@ -84,7 +85,13 @@ class HeapValues {
fun add(type: DataType, array: IntArray): Int {
// arrays are never shared
heap.add(HeapValue(type, null, array))
heap.add(HeapValue(type, null, array, null))
return heap.size-1
}
fun add(type: DataType, darray: DoubleArray): Int {
// arrays are never shared
heap.add(HeapValue(type, null, null, darray))
return heap.size-1
}
@ -102,21 +109,15 @@ class HeapValues {
}
}
fun update(heapId: Int, array: IntArray) {
when(heap[heapId].type){
DataType.ARRAY, DataType.ARRAY_W, DataType.MATRIX -> {
if(heap[heapId].array!!.size != array.size)
throw IllegalArgumentException("heap array length mismatch")
heap[heapId] = heap[heapId].copy(array=array)
}
else-> throw IllegalArgumentException("heap data type mismatch")
}
fun update(heapId: Int, heapval: HeapValue) {
heap[heapId] = heapval
}
fun get(heapId: Int): HeapValue = heap[heapId]
fun allStrings() = heap.asSequence().withIndex().filter { it.value.str!=null }.toList()
fun allArrays() = heap.asSequence().withIndex().filter { it.value.array!=null }.toList()
fun allDoubleArrays() = heap.asSequence().withIndex().filter { it.value.doubleArray!=null }.toList()
}
@ -146,7 +147,7 @@ class StackVmProgram(val name: String, val heap: HeapValues) {
throw CompilerException("string should already be in the heap")
Value(decl.datatype, litval.heapId)
}
DataType.ARRAY, DataType.ARRAY_W, DataType.MATRIX -> {
DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.MATRIX -> {
val litval = (decl.value as LiteralValue)
if(litval.heapId==null)
throw CompilerException("array/matrix should already be in the heap")
@ -458,7 +459,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
throw CompilerException("string should have been moved into heap ${lv.position}")
stackvmProg.instr(Opcode.PUSH, Value(lv.type, lv.heapId))
}
DataType.ARRAY, DataType.ARRAY_W, DataType.MATRIX -> {
DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.MATRIX -> {
if(lv.heapId==null)
throw CompilerException("array/matrix should have been moved into heap ${lv.position}")
stackvmProg.instr(Opcode.PUSH, Value(lv.type, lv.heapId))
@ -688,7 +689,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
}
}
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt")
DataType.ARRAY, DataType.ARRAY_W, DataType.MATRIX -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt")
DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.MATRIX -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt")
// todo: maybe if you assign byte or word to array/matrix, clear it with that value?
}
}
@ -799,18 +800,21 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
}
}
} else {
val litVal = loop.iterable as? LiteralValue
val ident = loop.iterable as? IdentifierReference
when {
litVal?.strvalue != null -> {
TODO("loop over string $litVal")
}
ident!=null -> {
val symbol = ident.targetStatement(namespace)
TODO("loop over symbol: ${ident.nameInSource} -> $symbol")
}
else -> throw CompilerException("loopvar is something strange ${loop.iterable}")
val iterableValue: LiteralValue?
if(loop.iterable is LiteralValue) {
if (!loop.iterable.isIterable(namespace, heap))
throw CompilerException("loop over something that isn't iterable ${loop.iterable}")
iterableValue = loop.iterable as LiteralValue
} else if(loop.iterable is IdentifierReference) {
val idRef = loop.iterable as IdentifierReference
iterableValue = ((idRef.targetStatement(namespace) as? VarDecl)?.value as? LiteralValue)
if(iterableValue!=null && !iterableValue.isIterable(namespace, heap))
throw CompilerException("loop over something that isn't iterable ${loop.iterable}")
} else {
throw CompilerException("loopvar is something strange ${loop.iterable}")
}
TODO("LOOP OVER ITERABLE VALUE (array/matrix/string) $iterableValue")
}
}

View File

@ -54,6 +54,8 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
data class Mflpt5(val b0: Short, val b1: Short, val b2: Short, val b3: Short, val b4: Short) {
companion object {
const val MemorySize = 5
val zero = Mflpt5(0, 0,0,0,0)
fun fromNumber(num: Number): Mflpt5 {
// see https://en.wikipedia.org/wiki/Microsoft_Binary_Format

View File

@ -33,17 +33,17 @@ val BuiltinFunctions = mapOf(
"sqrt" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", listOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::sqrt) },
"rad" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", listOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::toRadians) },
"deg" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", listOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArg(a, p, n, h, Math::toDegrees) },
"avg" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", listOf(DataType.ARRAY, DataType.ARRAY_W, DataType.MATRIX))), DataType.FLOAT, ::builtinAvg),
"avg" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", listOf(DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.MATRIX))), DataType.FLOAT, ::builtinAvg),
"abs" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", listOf(DataType.FLOAT))), DataType.FLOAT, ::builtinAbs),
"round" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", listOf(DataType.FLOAT))), null) { a, p, n, h -> oneDoubleArgOutputInt(a, p, n, h, Math::round) }, // type depends on arg
"floor" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", listOf(DataType.FLOAT))), null) { a, p, n, h -> oneDoubleArgOutputInt(a, p, n, h, Math::floor) }, // type depends on arg
"ceil" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", listOf(DataType.FLOAT))), null) { a, p, n, h -> oneDoubleArgOutputInt(a, p, n, h, Math::ceil) }, // type depends on arg
"max" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", listOf(DataType.ARRAY, DataType.ARRAY_W, DataType.MATRIX))), null) { a, p, n, h -> collectionArgOutputNumber(a, p, n, h) { it.max()!! }}, // type depends on args
"min" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", listOf(DataType.ARRAY, DataType.ARRAY_W, DataType.MATRIX))), null) { a, p, n, h -> collectionArgOutputNumber(a, p, n, h) { it.min()!! }}, // type depends on args
"sum" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", listOf(DataType.ARRAY, DataType.ARRAY_W, DataType.MATRIX))), null) { a, p, n, h -> collectionArgOutputNumber(a, p, n, h) { it.sum() }}, // type depends on args
"len" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", listOf(DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS, DataType.ARRAY, DataType.ARRAY_W, DataType.MATRIX))), null, ::builtinLen), // type depends on args
"any" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", listOf(DataType.ARRAY, DataType.ARRAY_W, DataType.MATRIX))), DataType.BYTE) { a, p, n, h -> collectionArgOutputBoolean(a, p, n, h) { it.any { v -> v != 0.0} }},
"all" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", listOf(DataType.ARRAY, DataType.ARRAY_W, DataType.MATRIX))), DataType.BYTE) { a, p, n, h -> collectionArgOutputBoolean(a, p, n, h) { it.all { v -> v != 0.0} }},
"max" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", listOf(DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.MATRIX))), null) { a, p, n, h -> collectionArgOutputNumber(a, p, n, h) { it.max()!! }}, // type depends on args
"min" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", listOf(DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.MATRIX))), null) { a, p, n, h -> collectionArgOutputNumber(a, p, n, h) { it.min()!! }}, // type depends on args
"sum" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", listOf(DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.MATRIX))), null) { a, p, n, h -> collectionArgOutputNumber(a, p, n, h) { it.sum() }}, // type depends on args
"len" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", listOf(DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS, DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.MATRIX))), null, ::builtinLen), // type depends on args
"any" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", listOf(DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.MATRIX))), DataType.BYTE) { a, p, n, h -> collectionArgOutputBoolean(a, p, n, h) { it.any { v -> v != 0.0} }},
"all" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", listOf(DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.MATRIX))), DataType.BYTE) { a, p, n, h -> collectionArgOutputBoolean(a, p, n, h) { it.all { v -> v != 0.0} }},
"lsb" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", listOf(DataType.WORD))), DataType.BYTE) { a, p, n, h -> oneIntArgOutputInt(a, p, n, h) { x: Int -> x and 255 }},
"msb" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", listOf(DataType.WORD))), DataType.BYTE) { a, p, n, h -> oneIntArgOutputInt(a, p, n, h) { x: Int -> x ushr 8 and 255}},
"flt" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", listOf(DataType.BYTE, DataType.WORD))), DataType.FLOAT, ::builtinFlt),
@ -87,7 +87,7 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespa
fun datatypeFromListArg(arglist: IExpression): DataType {
if(arglist is LiteralValue) {
if(arglist.type==DataType.ARRAY || arglist.type==DataType.ARRAY_W || arglist.type==DataType.MATRIX) {
if(arglist.type==DataType.ARRAY || arglist.type==DataType.ARRAY_W || arglist.type==DataType.ARRAY_F || arglist.type==DataType.MATRIX) {
val dt = arglist.arrayvalue!!.map {it.resultingDatatype(namespace, heap)}
if(dt.any { it!=DataType.BYTE && it!=DataType.WORD && it!=DataType.FLOAT}) {
throw FatalAstException("fuction $function only accepts array of numeric values")
@ -104,6 +104,7 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespa
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> dt
DataType.ARRAY -> DataType.BYTE
DataType.ARRAY_W -> DataType.WORD
DataType.ARRAY_F -> DataType.FLOAT
DataType.MATRIX -> DataType.BYTE
null -> throw FatalAstException("function requires one argument which is an array $function")
}
@ -124,6 +125,7 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespa
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> DataType.BYTE
DataType.ARRAY -> DataType.BYTE
DataType.ARRAY_W -> DataType.WORD
DataType.ARRAY_F -> DataType.FLOAT
DataType.MATRIX -> DataType.BYTE
}
}
@ -142,6 +144,7 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespa
DataType.BYTE, DataType.WORD -> DataType.WORD
DataType.FLOAT -> DataType.FLOAT
DataType.ARRAY, DataType.ARRAY_W -> DataType.WORD
DataType.ARRAY_F -> DataType.FLOAT
DataType.MATRIX -> DataType.WORD
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> DataType.WORD
}
@ -306,15 +309,21 @@ private fun builtinLen(args: List<IExpression>, position: Position, namespace:IN
?: throw SyntaxError("len over weird argument ${args[0]}", position)
}
return when(argument.type) {
DataType.ARRAY, DataType.ARRAY_W -> {
DataType.ARRAY, DataType.ARRAY_W, DataType.MATRIX -> {
val arraySize = argument.arrayvalue?.size ?: heap.get(argument.heapId!!).array!!.size
numericLiteral(arraySize, args[0].position)
}
DataType.ARRAY_F -> {
val arraySize = argument.arrayvalue?.size ?: heap.get(argument.heapId!!).doubleArray!!.size
numericLiteral(arraySize, args[0].position)
}
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> {
val str = argument.strvalue ?: heap.get(argument.heapId!!).str!!
numericLiteral(str.length, args[0].position)
}
else -> throw SyntaxError("len of weird argument ${args[0]}", position)
DataType.BYTE,
DataType.WORD,
DataType.FLOAT -> throw SyntaxError("len of weird argument ${args[0]}", position)
}
}

View File

@ -2,6 +2,8 @@ package prog8.optimizing
import prog8.ast.*
import prog8.compiler.HeapValues
import prog8.compiler.target.c64.FLOAT_MAX_NEGATIVE
import prog8.compiler.target.c64.FLOAT_MAX_POSITIVE
import prog8.compiler.target.c64.Petscii
@ -52,12 +54,41 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
DataType.ARRAY, DataType.ARRAY_W, DataType.MATRIX -> {
val litval = decl.value as? LiteralValue
val size = decl.arrayspec!!.size()
if (size != null) {
if(litval!=null && litval.isArray) {
// array initializer value is an array already, keep as-is
if(litval.heapId!=null) {
if (decl.datatype == DataType.MATRIX && litval.type != DataType.MATRIX) {
val array = heap.get(litval.heapId).copy(type = DataType.MATRIX)
heap.update(litval.heapId, array)
}
}
} 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
val fillArray = IntArray(size) { _ -> fillvalue }
val heapId = heap.add(decl.datatype, fillArray)
val valType = if(decl.datatype==DataType.MATRIX) DataType.ARRAY else decl.datatype
decl.value = LiteralValue(valType, heapId = heapId, position = litval?.position ?: decl.position)
decl.value = LiteralValue(decl.datatype, heapId = heapId, position = litval?.position ?: decl.position)
}
}
DataType.ARRAY_F -> {
val litval = decl.value as? LiteralValue
val size = decl.arrayspec!!.size()
if(litval!=null && litval.isArray) {
// array initializer value is an array 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) {
// 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)
}
}
else -> return result
@ -66,21 +97,6 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
return result
}
private fun createArrayInitValue(decl: VarDecl, intvalue: Int, position: Position) {
val size = decl.arrayspec!!.size()
if (size != null) {
val newArray = Array<IExpression>(size) { _ ->
if (decl.datatype == DataType.ARRAY)
LiteralValue(DataType.BYTE, bytevalue = intvalue.toShort(), position = position)
else
LiteralValue(DataType.WORD, wordvalue = intvalue, position = position)
}
decl.value = LiteralValue(decl.datatype, arrayvalue = newArray, position = position)
} else {
addError(SyntaxError("array size must be a constant integer value", position))
}
}
/**
* replace identifiers that refer to const value, with the value itself
*/
@ -289,48 +305,54 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
}
} else if(literalValue.arrayvalue!=null) {
val newArray = literalValue.arrayvalue.map { it.process(this) }.toTypedArray()
// determine if the values are all bytes or that we need a word array instead
var arrayDt = DataType.ARRAY
var allElementsAreConstant = true
for (expr in newArray) {
allElementsAreConstant = allElementsAreConstant and (expr is LiteralValue)
val valueDt = expr.resultingDatatype(namespace, heap)
if(valueDt==DataType.BYTE)
continue
else {
arrayDt = DataType.ARRAY_W
break
}
}
if(newArray.any { it.resultingDatatype(namespace, heap) == DataType.WORD })
arrayDt = DataType.ARRAY_W
if(newArray.any { it.resultingDatatype(namespace, heap) == DataType.FLOAT })
arrayDt = DataType.ARRAY_F
// 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 array = newArray.map {
val litval = it as? LiteralValue
if(litval==null) {
addError(ExpressionError("array/matrix literal can contain only constant values", literalValue.position))
return super.process(literalValue)
}
if(litval.bytevalue==null && litval.wordvalue==null) {
if(arrayDt==DataType.ARRAY)
addError(ExpressionError("byte array elements must all be integers 0..255", literalValue.position))
else
addError(ExpressionError("word array elements must all be integers 0..65535", literalValue.position))
return super.process(literalValue)
}
val integer = litval.asIntegerValue!!
if(arrayDt==DataType.ARRAY && integer !in 0..255) {
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 || arrayDt==DataType.MATRIX) {
// all values should be bytes
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)
} else if(arrayDt==DataType.ARRAY_W && integer !in 0..65535) {
}
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_W) {
// 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)
}
integer
}.toIntArray()
val heapId = heap.add(arrayDt, array)
val newValue = LiteralValue(arrayDt, heapId=heapId, position = literalValue.position)
return super.process(newValue)
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))
}
@ -338,6 +360,7 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
val newValue = LiteralValue(arrayDt, arrayvalue = newArray, position = literalValue.position)
return super.process(newValue)
}
return super.process(literalValue)
}
@ -349,9 +372,11 @@ class ConstantFolding(private val namespace: INameScope, private val heap: HeapV
if(arrayIndexedExpression.identifier!=null) {
val x = (arrayIndexedExpression.array.x as LiteralValue).asIntegerValue!!
val y = (arrayIndexedExpression.array.y as LiteralValue).asIntegerValue!!
val variable = arrayIndexedExpression.identifier.targetStatement(namespace) as VarDecl
val index = x + y*(variable.arrayspec!!.x as LiteralValue).asIntegerValue!!
arrayIndexedExpression.array = ArraySpec(LiteralValue.optimalInteger(index, arrayIndexedExpression.array.position), null, arrayIndexedExpression.array.position)
val variable = arrayIndexedExpression.identifier.targetStatement(namespace) as? VarDecl
if(variable!=null) {
val index = x + y * (variable.arrayspec!!.x as LiteralValue).asIntegerValue!!
arrayIndexedExpression.array = ArraySpec(LiteralValue.optimalInteger(index, arrayIndexedExpression.array.position), null, arrayIndexedExpression.array.position)
}
}
}
}

View File

@ -67,7 +67,12 @@ class Program (val name: String,
val intarray = numbers.map{number->number.trim().toInt()}.toIntArray()
heap.add(it.second, intarray)
}
else -> throw VmExecutionException("invalid heap value type $it.second")
DataType.ARRAY_F -> {
val numbers = it.third.substring(1, it.third.length-1).split(',')
val doublearray = numbers.map{number->number.trim().toDouble()}.toDoubleArray()
heap.add(it.second, doublearray)
}
DataType.BYTE, DataType.WORD, DataType.FLOAT -> throw VmExecutionException("invalid heap value type ${it.second}")
}
}
}
@ -172,6 +177,7 @@ class Program (val name: String,
}
DataType.ARRAY,
DataType.ARRAY_W,
DataType.ARRAY_F,
DataType.MATRIX -> {
if(!valueStr.startsWith("heap:"))
throw VmExecutionException("invalid array/matrix value, should be a heap reference")
@ -281,6 +287,9 @@ class Program (val name: String,
heap.allArrays().forEach {
out.println("${it.index} ${it.value.type.toString().toLowerCase()} ${it.value.array!!.toList()}")
}
heap.allDoubleArrays().forEach {
out.println("${it.index} ${it.value.type.toString().toLowerCase()} ${it.value.doubleArray!!.toList()}")
}
out.println("%end_heap")
out.println("%variables")
// just flatten all block vars into one global list for now...

View File

@ -483,6 +483,7 @@ class StackVm(private var traceOutputFile: String?) {
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> print(heap.get(value.heapId).str)
DataType.ARRAY, DataType.ARRAY_W -> print(heap.get(value.heapId).array!!.toList())
DataType.MATRIX -> print(heap.get(value.heapId).array!!.toList())
DataType.ARRAY_F -> print(heap.get(value.heapId).doubleArray!!.toList())
}
}
Syscall.INPUT_STR -> {
@ -573,7 +574,8 @@ class StackVm(private var traceOutputFile: String?) {
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> DataType.BYTE
DataType.ARRAY, DataType.MATRIX -> DataType.BYTE
DataType.ARRAY_W -> DataType.WORD
else -> throw VmExecutionException("uniterable value $iterable")
DataType.ARRAY_F -> DataType.FLOAT
DataType.BYTE, DataType.WORD, DataType.FLOAT -> throw VmExecutionException("uniterable value $iterable")
}
if(value.str!=null) {
val result = Petscii.encodePetscii(value.str.max().toString(), true)[0]
@ -590,7 +592,8 @@ class StackVm(private var traceOutputFile: String?) {
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> DataType.BYTE
DataType.ARRAY, DataType.MATRIX -> DataType.BYTE
DataType.ARRAY_W -> DataType.WORD
else -> throw VmExecutionException("uniterable value $iterable")
DataType.ARRAY_F -> DataType.FLOAT
DataType.BYTE, DataType.WORD, DataType.FLOAT -> throw VmExecutionException("uniterable value $iterable")
}
if(value.str!=null) {
val result = Petscii.encodePetscii(value.str.min().toString(), true)[0]
@ -917,11 +920,17 @@ class StackVm(private var traceOutputFile: String?) {
} else {
// get indexed element from the array
val array = heap.get(variable.heapId)
val result = array.array!![index]
when(array.type) {
DataType.ARRAY, DataType.MATRIX -> evalstack.push(Value(DataType.BYTE, result))
DataType.ARRAY_W -> evalstack.push(Value(DataType.WORD, result))
else -> throw VmExecutionException("not a proper array/matrix var")
DataType.ARRAY, DataType.MATRIX -> evalstack.push(Value(DataType.BYTE, array.array!![index]))
DataType.ARRAY_W -> evalstack.push(Value(DataType.WORD, array.array!![index]))
DataType.ARRAY_F -> evalstack.push(Value(DataType.FLOAT, array.doubleArray!![index]))
DataType.BYTE,
DataType.WORD,
DataType.FLOAT,
DataType.STR,
DataType.STR_P,
DataType.STR_S,
DataType.STR_PS -> throw VmExecutionException("not a proper array/matrix var")
}
}
}

View File

@ -74,7 +74,8 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
return false
if(type==other.type) {
return when (type) {
DataType.STR, DataType.STR_S, DataType.STR_P, DataType.STR_PS, DataType.ARRAY, DataType.ARRAY_W, DataType.MATRIX -> heapId==other.heapId
DataType.STR, DataType.STR_S, DataType.STR_P, DataType.STR_PS,
DataType.ARRAY, DataType.ARRAY_W, DataType.ARRAY_F, DataType.MATRIX -> heapId==other.heapId
DataType.BYTE, DataType.WORD, DataType.FLOAT -> compareTo(other)==0
}
}

View File

@ -86,9 +86,17 @@ class TestStackVmValue {
assertFalse(sameValueAndType(Value(DataType.STR, 999), Value(DataType.STR_P, 999)))
assertFalse(sameValueAndType(Value(DataType.STR, 999), Value(DataType.STR, 222)))
assertTrue(sameValueAndType(Value(DataType.ARRAY, 99), Value(DataType.ARRAY, 99)))
assertFalse(sameValueAndType(Value(DataType.ARRAY, 99), Value(DataType.MATRIX, 99)))
assertFalse(sameValueAndType(Value(DataType.ARRAY, 99), Value(DataType.ARRAY, 22)))
assertTrue(sameValueAndType(Value(DataType.ARRAY_W, 999), Value(DataType.ARRAY_W, 999)))
assertFalse(sameValueAndType(Value(DataType.ARRAY_W, 999), Value(DataType.MATRIX, 999)))
assertFalse(sameValueAndType(Value(DataType.ARRAY_W, 999), Value(DataType.ARRAY_W, 222)))
assertTrue(sameValueAndType(Value(DataType.ARRAY_F, 999), Value(DataType.ARRAY_F, 999)))
assertFalse(sameValueAndType(Value(DataType.ARRAY_F, 999), Value(DataType.ARRAY_W, 999)))
assertFalse(sameValueAndType(Value(DataType.ARRAY_F, 999), Value(DataType.ARRAY_F, 222)))
}
@Test

View File

@ -180,7 +180,8 @@ Values will usually be part of an expression or assignment statement::
byte counter = 42 ; variable of size 8 bits, with initial value 42
Array and Matrix (2-dimensional array) types are also supported like this::
Array and Matrix (2-dimensional array) types are also supported.
Arrays can be made of bytes, words and floats. Matrixes can oly be made of bytes::
byte[4] array = [1, 2, 3, 4] ; initialize the array
byte[99] array = 255 ; initialize array with all 255's [255, 255, 255, 255, ...]
@ -313,7 +314,8 @@ Loops
-----
The *for*-loop is used to let a variable (or register) iterate over a range of values. Iteration is done in steps of 1, but you can change this.
The loop variable must be declared as byte or word earlier. Floating point iteration is not supported.
The loop variable must be declared as byte or word earlier. Floating point iteration is not supported,
if you want to loop over a floating-point array, use a loop with an integer index variable instead.
The *while*-loop is used to repeat a piece of code while a certain condition is still true.
The *repeat--until* loop is used to repeat a piece of code until a certain condition is true.

View File

@ -235,6 +235,7 @@ type identifier type storage size example var declara
stored in 5-byte cbm MFLPT format
``byte[x]`` unsigned byte array x bytes ``byte[4] myvar = [1, 2, 3, 4]``
``word[x]`` unsigned word array 2*x bytes ``word[4] myvar = [1, 2, 3, 4]``
``float[x]`` floating-point array 5*x bytes ``float[4] myvar = [1.1, 2.2, 3.3, 4.4]``
``byte[x,y]`` unsigned byte matrix x*y bytes ``byte[40,25] myvar = @todo``
word-matrix not supported
``str`` string (petscii) varies ``str myvar = "hello."``
@ -450,9 +451,9 @@ Loops
for loop
^^^^^^^^
The loop variable must be a register or a variable defined in the local scope.
The loop variable must be a register or a byte/word variable defined in the local scope.
The expression that you loop over can be anything that supports iteration (such as ranges like ``0 to 100``,
array variables and strings).
array variables and strings) *except* floating-point arrays (because a floating-point loop variable is not supported).
You can use a single statement, or a statement block like in the example below::