fix array problems, limit size of arrays.

This commit is contained in:
Irmen de Jong 2018-10-23 00:52:51 +02:00
parent b8f3f942d4
commit a825bbff96
8 changed files with 110 additions and 49 deletions

View File

@ -1,19 +1,46 @@
%import c64utils
%output prg
%launcher basic
; %import c64utils
%option enable_floats
%output raw
%launcher none
~ main {
str str1 = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789abcde"
str_p str2 = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789abcde"
str_s str3 = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789abcde"
str_ps str4 = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789abcde"
byte[256] barr2 = 2 ; @todo weird init error
byte[256] barr3 = 3 ; @todo weird init error
byte[256] barr2b ; @todo weird init error
byte[256] barr3b ; @todo weird init error
ubyte[256] ubarr2 = 2 ; @todo weird init error
ubyte[256] ubarr3 = 3 ; @todo weird init error
ubyte[256] ubarr2b ; @todo weird init error
ubyte[256] ubarr3b ; @todo weird init error
word[128] warr2 = 2 ; @todo weird init error
word[128] warr3 = 3 ; @todo weird init error
word[128] warr2b ; @todo weird init error
word[128] warr3b ; @todo weird init error
uword[128] wbarr2 = 2 ; @todo weird init error
uword[128] wbarr3 = 3 ; @todo weird init error
uword[128] wbarr2b ; @todo weird init error
uword[128] wbarr3b ; @todo weird init error
byte[256,257] bmatrix ; @todo weird init error
ubyte[256,257] ubmatrix ; @todo weird init error
; @todo later: allow for arrays/matrixes with length > 256 (uword index)
sub start() {
c64.VMCSB |= 2 ; activate lowercase charset
; greeting
c64scr.print_string("Enter your name: ")
return
}
}

View File

@ -565,7 +565,7 @@ class ArraySpec(var x: IExpression, var y: IExpression?, override val position:
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)
return ArraySpec(LiteralValue.optimalNumeric(arraySize, v.position), null, v.position)
}
}
@ -1012,6 +1012,7 @@ class LiteralValue(val type: DataType,
val asIntegerValue: Int? = when {
bytevalue!=null -> bytevalue.toInt()
wordvalue!=null -> wordvalue
floatvalue!=null -> floor(floatvalue).toInt()
else -> null
}

View File

@ -45,6 +45,11 @@ class AstChecker(private val namespace: INameScope,
private val compilerOptions: CompilationOptions,
private val heap: HeapValues) : IAstProcessor {
private val checkResult: MutableList<AstException> = mutableListOf()
private val heapStringSentinel: Int
init {
val stringSentinel = heap.allStrings().firstOrNull {it.value.str==""}
heapStringSentinel = stringSentinel?.index ?: heap.add(DataType.STR, "")
}
fun result(): List<AstException> {
return checkResult
@ -402,14 +407,18 @@ class AstChecker(private val namespace: INameScope,
when(decl.type) {
VarDeclType.VAR, VarDeclType.CONST -> {
if (decl.value == null) {
when {
decl.datatype in NumericDatatypes -> {
// initialize numeric var with value zero by default.
val litVal = LiteralValue(DataType.UBYTE, 0, position = decl.position)
litVal.parent = decl
decl.value = litVal
}
else -> err("var/const declaration needs a compile-time constant initializer value for type ${decl.datatype}") // const fold should have provided it!
if (decl.datatype in NumericDatatypes) {
// initialize numeric var with value zero by default.
val litVal = LiteralValue(DataType.UBYTE, 0, position = decl.position)
litVal.parent = decl
decl.value = litVal
}
else if(decl.type==VarDeclType.VAR) {
val litVal = LiteralValue(decl.datatype, heapId = heapStringSentinel, position=decl.position) // point to the sentinel heap value instead
litVal.parent=decl
decl.value = litVal
} else {
err("var/const declaration needs a compile-time constant initializer value for type ${decl.datatype}") // const fold should have provided it!
}
return super.process(decl)
}
@ -812,15 +821,17 @@ class AstChecker(private val namespace: INameScope,
if(!value.isString)
return err("string value expected")
val str = value.strvalue ?: heap.get(value.heapId!!).str!!
if (str.isEmpty() || str.length > 255)
return err("string length must be 1 to 255")
if (str.length > 255)
return err("string length must be 0-255")
}
DataType.ARRAY_UB, DataType.ARRAY_B -> {
// value may be either a single byte, or a byte array (of all constant values)
if(value.type==targetDt) {
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).array!!.size
val arraySpecSize = arrayspec.size()
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).arraysize
if(arraySpecSize!=null && arraySpecSize>0) {
if(arraySpecSize<1 || arraySpecSize>256)
return err("byte array length must be 1-256")
val constX = arrayspec.x.constValue(namespace, heap)
if(constX?.asIntegerValue==null)
return err("array size specifier must be constant integer value")
@ -829,15 +840,18 @@ class AstChecker(private val namespace: INameScope,
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
return true
}
return err("invalid byte array size, must be 1-256")
}
return err("invalid array initialization value ${value.type}, expected $targetDt")
return err("invalid byte 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==targetDt) {
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).array!!.size
val arraySpecSize = arrayspec.size()
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).arraysize
if(arraySpecSize!=null && arraySpecSize>0) {
if(arraySpecSize<1 || arraySpecSize>128)
return err("word array length must be 1-128")
val constX = arrayspec.x.constValue(namespace, heap)
if(constX?.asIntegerValue==null)
return err("array size specifier must be constant integer value")
@ -846,8 +860,9 @@ class AstChecker(private val namespace: INameScope,
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
return true
}
return err("invalid word array size, must be 1-128")
}
return err("invalid array initialization value ${value.type}, expected $targetDt")
return err("invalid word array initialization value ${value.type}, expected $targetDt")
}
DataType.ARRAY_F -> {
// value may be either a single float, or a float array
@ -855,13 +870,17 @@ class AstChecker(private val namespace: INameScope,
val arraySize = value.arrayvalue?.size ?: heap.get(value.heapId!!).doubleArray!!.size
val arraySpecSize = arrayspec.size()
if(arraySpecSize!=null && arraySpecSize>0) {
if(arraySpecSize < 1 || arraySpecSize>51)
return err("float array length must be 1-51")
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
return err("invalid float array size, must be 1-51")
// 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()
@ -871,7 +890,7 @@ class AstChecker(private val namespace: INameScope,
return err("floating point value overflow")
return true
}
return err("invalid array initialization value ${value.type}, expected $targetDt")
return err("invalid float 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)
@ -880,6 +899,8 @@ class AstChecker(private val namespace: INameScope,
(targetDt==DataType.MATRIX_B && value.type==DataType.ARRAY_B)) {
val arraySpecSize = arrayspec.size()
if(arraySpecSize!=null && arraySpecSize>0) {
if(arraySpecSize<1 || arraySpecSize>256)
return err("invalid matrix size, must be 1-256")
val constX = arrayspec.x.constValue(namespace, heap)
val constY = arrayspec.y?.constValue(namespace, heap)
if (constX?.asIntegerValue == null || (constY!=null && constY.asIntegerValue == null))
@ -891,6 +912,7 @@ class AstChecker(private val namespace: INameScope,
return err("initializer matrix size mismatch (expecting $expectedSize, got ${matrix.size} elements)")
return true
}
return err("invalid matrix size, must be 1-256")
}
return err("invalid matrix initialization value of type ${value.type} - expecting byte array")
}

View File

@ -87,6 +87,9 @@ class AstIdentifiersChecker : IAstProcessor {
}
// inject subroutine params as local variables (if they're not there yet) (for non-kernel subroutines)
// NOTE:
// - numeric types BYTE and WORD and FLOAT are passed by value;
// - strings, arrays, matrices are passed by reference (their 16-bit address is passed as an uword parameter)
if(subroutine.asmAddress==null) {
subroutine.parameters
.filter { !definedNames.containsKey(it.name) }

View File

@ -81,8 +81,8 @@ class HeapValues {
fun size(): Int = heap.size
fun add(type: DataType, str: String): Int {
if (str.isEmpty() || str.length > 255)
throw IllegalArgumentException("string length must be 1-255")
if (str.length > 255)
throw IllegalArgumentException("string length must be 0-255")
// strings are 'interned' and shared if they're the same
val value = HeapValue(type, str, null, null)
@ -1416,11 +1416,11 @@ private class StatementTranslator(private val prog: IntermediateProgram,
DataType.ARRAY_UB, DataType.ARRAY_B,
DataType.ARRAY_UW, DataType.ARRAY_W,
DataType.MATRIX_UB, DataType.MATRIX_B -> {
numElements = iterableValue.arrayvalue?.size ?: heap.get(iterableValue.heapId!!).array!!.size
numElements = iterableValue.arrayvalue?.size ?: heap.get(iterableValue.heapId!!).arraysize
indexVar = if(numElements>255) "XY" else "X"
}
DataType.ARRAY_F -> {
numElements = iterableValue.arrayvalue?.size ?: heap.get(iterableValue.heapId!!).doubleArray!!.size
numElements = iterableValue.arrayvalue?.size ?: heap.get(iterableValue.heapId!!).arraysize
indexVar = if(numElements>255) "XY" else "X"
}
}

View File

@ -43,7 +43,7 @@ val BuiltinFunctions = mapOf(
"round" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.WORD) { a, p, n, h -> oneDoubleArgOutputWord(a, p, n, h, Math::round) },
"floor" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.WORD) { a, p, n, h -> oneDoubleArgOutputWord(a, p, n, h, Math::floor) },
"ceil" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.WORD) { a, p, n, h -> oneDoubleArgOutputWord(a, p, n, h, Math::ceil) },
"len" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", IterableDatatypes)), DataType.UWORD, ::builtinLen),
"len" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", IterableDatatypes)), DataType.UBYTE, ::builtinLen),
"any" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, n, h -> collectionArgOutputBoolean(a, p, n, h) { it.any { v -> v != 0.0} }},
"all" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, n, h -> collectionArgOutputBoolean(a, p, n, h) { it.all { v -> v != 0.0} }},
"lsb" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, n, h -> oneIntArgOutputInt(a, p, n, h) { x: Int -> x and 255 }},
@ -336,16 +336,18 @@ private fun builtinLen(args: List<IExpression>, position: Position, namespace:IN
if(argument==null) {
if(args[0] !is IdentifierReference)
throw SyntaxError("len over weird argument ${args[0]}", position)
argument = ((args[0] as IdentifierReference).targetStatement(namespace) as? VarDecl)?.value?.constValue(namespace, heap)
?: throw SyntaxError("len over weird argument ${args[0]}", position)
val target = (args[0] as IdentifierReference).targetStatement(namespace)
val argValue = (target as? VarDecl)?.value
argument = argValue?.constValue(namespace, heap)
?: throw NotConstArgumentException()
}
return when(argument.type) {
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.MATRIX_UB, DataType.MATRIX_B -> {
val arraySize = argument.arrayvalue?.size ?: heap.get(argument.heapId!!).array!!.size
val arraySize = argument.arrayvalue?.size ?: heap.get(argument.heapId!!).arraysize
LiteralValue(DataType.UWORD, wordvalue=arraySize, position=args[0].position)
}
DataType.ARRAY_F -> {
val arraySize = argument.arrayvalue?.size ?: heap.get(argument.heapId!!).doubleArray!!.size
val arraySize = argument.arrayvalue?.size ?: heap.get(argument.heapId!!).arraysize
LiteralValue(DataType.UWORD, wordvalue=arraySize, position=args[0].position)
}
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> {

View File

@ -184,14 +184,14 @@ When declaring a numeric variable it is possible to specify the initial value, i
For other data types it is required to specify that initial value it should get.
Values will usually be part of an expression or assignment statement::
12345 ; integer number
$aa43 ; hex integer number
%100101 ; binary integer number
"Hi, I am a string" ; text string
'a' ; petscii value (byte) for the letter a
-33.456e52 ; floating point number
12345 ; integer number
$aa43 ; hex integer number
%100101 ; binary integer number
"Hi, I am a string" ; text string
'a' ; petscii value (byte) for the letter a
-33.456e52 ; floating point number
byte counter = 42 ; variable of size 8 bits, with initial value 42
byte counter = 42 ; variable of size 8 bits, with initial value 42
Array and Matrix (2-dimensional array) types are also supported.
@ -207,9 +207,14 @@ Arrays can be made of bytes, words and floats. Matrixes can oly be made of bytes
value = matrix[4,2] ; the byte at the 5th column and 3rd row in the matrix
char = string[4] ; the fifth character (=byte) in the string
.. note::
Right now, the array and matrix size should be small enough to be indexable by a single byte index.
This means byte arrays and matrixes should be <= 256 elements, word arrays <= 128 elements, and float
arrays <= 51 elements. This limit may be lifted in a future version.
Note that the various keywords for the data type and variable type (``byte``, ``word``, ``const``, etc.)
cannot be used as *identifiers* elsewhere. You can't make a variable, block or subroutine with the name ``byte``
can't be used as *identifiers* elsewhere. You can't make a variable, block or subroutine with the name ``byte``
for instance.
.. todo::
@ -342,6 +347,7 @@ You can also create loops by using the ``goto`` statement, but this should usual
The value of the loop variable or register after executing the loop *is undefined*. Don't use it immediately
after the loop without first assigning a new value to it!
(this is an optimization issue to avoid having to deal with mostly useless post-loop logic to adjust the loop variable's value)
Loop variables that are declared inline are scoped in the loop body so they're not accessible at all after the loop finishes.
Conditional Execution

View File

@ -264,7 +264,7 @@ asmsub GETADRAY () -> clobbers(X) -> (uword @ AY) {
}
asmsub copy_mflt (source: uword @ XY) -> clobbers(A,Y) -> () {
asmsub copy_mflt (source: uword @ XY) -> clobbers(A) -> () {
; ---- copy a 5 byte MFLT floating point variable to another place
; input: X/Y = source address, c64.SCRATCH_ZPWORD1 = destination address
%asm {{
@ -364,7 +364,7 @@ asmsub float_sub_SW1_from_XY (mflt: uword @ XY) -> clobbers(A,X,Y) -> () {
; ---- this block contains (character) Screen and text I/O related functions ----
asmsub clear_screen (char: ubyte @ A, color: ubyte @ Y) -> clobbers() -> () {
asmsub clear_screen (char: ubyte @ A, color: ubyte @ Y) -> clobbers(A,X) -> () {
; ---- clear the character screen with the given fill character and character color.
; (assumes screen is at $0400, could be altered in the future with self-modifying code)
; @todo some byte var to set the SCREEN ADDR HI BYTE
@ -607,7 +607,7 @@ _scroll_screen ; scroll the screen memory
asmsub print_string (address: uword @ XY) -> clobbers(A,Y) -> () {
asmsub print_string (text: str @ XY) -> clobbers(A,Y) -> () {
; ---- print null terminated string from X/Y
; note: the compiler contains an optimization that will replace
; a call to this subroutine with a string argument of just one char,
@ -626,7 +626,7 @@ asmsub print_string (address: uword @ XY) -> clobbers(A,Y) -> () {
}
asmsub print_pstring (address: uword @ XY) -> clobbers(A,X) -> (ubyte @ Y) {
asmsub print_pstring (text: str_p @ XY) -> clobbers(A,X) -> (ubyte @ Y) {
; ---- print pstring (length as first byte) from X/Y, returns str len in Y
%asm {{
stx c64.SCRATCH_ZP1