mirror of
https://github.com/irmen/prog8.git
synced 2025-01-11 13:29:45 +00:00
fix array problems, limit size of arrays.
This commit is contained in:
parent
b8f3f942d4
commit
a825bbff96
@ -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
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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) }
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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 -> {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user