diff --git a/.idea/libraries/KotlinJavaRuntime.xml b/.idea/libraries/KotlinJavaRuntime.xml
index 98e799522..cf8a55908 100644
--- a/.idea/libraries/KotlinJavaRuntime.xml
+++ b/.idea/libraries/KotlinJavaRuntime.xml
@@ -1,23 +1,23 @@
-
+
-
-
+
+
-
+
-
-
+
+
-
+
-
-
+
+
-
+
\ No newline at end of file
diff --git a/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt b/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt
index b578073b6..2dbd2bf77 100644
--- a/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt
+++ b/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt
@@ -268,7 +268,6 @@ class VarConstantValueTypeAdjuster(
// Replace all constant identifiers with their actual value,
-// and the array var initializer values and sizes.
// This is needed because further constant optimizations depend on those.
internal class ConstantIdentifierReplacer(
private val program: Program,
@@ -421,104 +420,41 @@ internal class ConstantIdentifierReplacer(
return null
}
- // convert the initializer range expression from a range or int, to an actual array.
+ val rangeExpr = decl.value as? RangeExpression ?: return null
+
+ // convert the initializer range expression from a range, to an actual array literal.
+ val declArraySize = decl.arraysize?.constIndex()
+ val constRange = rangeExpr.toConstantIntegerRange()
+ if(constRange?.isEmpty()==true) {
+ if(constRange.first>constRange.last && constRange.step>=0)
+ errors.err("descending range with positive step", decl.value?.position!!)
+ else if(constRange.first {
- val rangeExpr = decl.value as? RangeExpression
- if(rangeExpr!=null) {
- val constRange = rangeExpr.toConstantIntegerRange()
- if(constRange?.isEmpty()==true) {
- if(constRange.first>constRange.last && constRange.step>=0)
- errors.err("descending range with positive step", decl.value?.position!!)
- else if(constRange.first {
- if(fillvalue !in 0..255)
- errors.err("ubyte value overflow", numericLv.position)
- }
- DataType.ARRAY_B -> {
- if(fillvalue !in -128..127)
- errors.err("byte value overflow", numericLv.position)
- }
- DataType.ARRAY_UW -> {
- if(fillvalue !in 0..65535)
- errors.err("uword value overflow", numericLv.position)
- }
- DataType.ARRAY_W -> {
- if(fillvalue !in -32768..32767)
- errors.err("word value overflow", numericLv.position)
- }
- else -> {}
- }
- // create the array itself, filled with the fillvalue.
- val array = Array(size) {fillvalue}.map { NumericLiteral(ArrayToElementTypes.getValue(decl.datatype), it.toDouble(), numericLv.position) }.toTypedArray()
- return ArrayLiteral(InferredTypes.InferredType.known(decl.datatype), array, position = numericLv.position)
- }
- }
- DataType.ARRAY_F -> {
- val rangeExpr = decl.value as? RangeExpression
- if(rangeExpr!=null) {
- // convert the initializer range expression to an actual array of floats
- val declArraySize = decl.arraysize?.constIndex()
- if(declArraySize!=null && declArraySize!=rangeExpr.size())
- errors.err("range expression size (${rangeExpr.size()}) doesn't match declared array size ($declArraySize)", decl.value?.position!!)
- val constRange = rangeExpr.toConstantIntegerRange()
- if(constRange!=null) {
- return ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_F),
- constRange.map { NumericLiteral(DataType.FLOAT, it.toDouble(), decl.value!!.position) }.toTypedArray(),
+ if(declArraySize!=null && declArraySize!=rangeExpr.size())
+ errors.err("range expression size (${rangeExpr.size()}) doesn't match declared array size ($declArraySize)", decl.value?.position!!)
+ if(constRange!=null) {
+ val eltType = rangeExpr.inferType(program).getOr(DataType.UBYTE)
+ return if(eltType in ByteDatatypes) {
+ ArrayLiteral(InferredTypes.InferredType.known(decl.datatype),
+ constRange.map { NumericLiteral(eltType, it.toDouble(), decl.value!!.position) }.toTypedArray(),
+ position = decl.value!!.position)
+ } else {
+ ArrayLiteral(InferredTypes.InferredType.known(decl.datatype),
+ constRange.map { NumericLiteral(eltType, it.toDouble(), decl.value!!.position) }.toTypedArray(),
position = decl.value!!.position)
}
}
-
- val numericLv = decl.value as? NumericLiteral
- val size = decl.arraysize?.constIndex() ?: return null
- if(rangeExpr==null && numericLv!=null) {
- // arraysize initializer is a single int, and we know the array size.
- val fillvalue = numericLv.number
- if (fillvalue < options.compTarget.machine.FLOAT_MAX_NEGATIVE || fillvalue > options.compTarget.machine.FLOAT_MAX_POSITIVE)
- errors.err("float value overflow", numericLv.position)
- else {
- val array = Array(size) {fillvalue}.map { NumericLiteral(DataType.FLOAT, it, numericLv.position) }.toTypedArray()
- return ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_F), array, position = numericLv.position)
- }
- }
}
- DataType.ARRAY_BOOL -> {
- val numericLv = decl.value as? NumericLiteral
- val size = decl.arraysize?.constIndex() ?: return null
- if(numericLv!=null) {
- // arraysize initializer is a single value, and we know the array size.
- if(numericLv.type!=DataType.BOOL) {
- errors.err("initializer value is not a boolean", numericLv.position)
- return null
- }
- val array = Array(size) {numericLv.number}.map { NumericLiteral(DataType.BOOL, it, numericLv.position) }.toTypedArray()
- return ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_BOOL), array, position = numericLv.position)
+ DataType.ARRAY_F -> {
+ if(declArraySize!=null && declArraySize!=rangeExpr.size())
+ errors.err("range expression size (${rangeExpr.size()}) doesn't match declared array size ($declArraySize)", decl.value?.position!!)
+ if(constRange!=null) {
+ return ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_F),
+ constRange.map { NumericLiteral(DataType.FLOAT, it.toDouble(), decl.value!!.position) }.toTypedArray(),
+ position = decl.value!!.position)
}
}
else -> return null
diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt
index cf29e5563..463b2b465 100644
--- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt
+++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt
@@ -716,12 +716,8 @@ internal class AstChecker(private val program: Program,
if(decl.isArray && decl.arraysize==null) {
if(decl.type== VarDeclType.MEMORY)
err("memory mapped array must have a size specification")
- if(decl.value==null) {
- valueerr("array variable is missing a size specification or an initialization value")
- return
- }
- if(decl.value is NumericLiteral) {
- valueerr("unsized array declaration cannot use a single literal initialization value")
+ if(decl.value==null || decl.value is NumericLiteral) {
+ err("array variable is missing a size specification")
return
}
if(decl.value is RangeExpression)
@@ -802,6 +798,30 @@ internal class AstChecker(private val program: Program,
// array length limits and constant lenghts
if(decl.isArray) {
+
+ if(decl.type!=VarDeclType.MEMORY) {
+ // memory-mapped arrays are initialized with their address, but any other array needs a range or array literal value.
+
+ if (decl.value!=null && decl.value !is ArrayLiteral && decl.value !is RangeExpression) {
+ var suggestion: String? = null
+ val arraysize = decl.arraysize?.constIndex()
+ val numericvalue = decl.value?.constValue(program)
+ if (numericvalue != null && arraysize != null) {
+ when (numericvalue.type) {
+ in IntegerDatatypes -> suggestion = "[${numericvalue.number.toInt()}] * $arraysize"
+ DataType.FLOAT -> suggestion = "[${numericvalue.number}] * $arraysize"
+ DataType.BOOL -> suggestion = "[${numericvalue.asBooleanValue}] * $arraysize"
+ else -> {}
+ }
+ }
+
+ if (suggestion != null)
+ valueerr("array initialization value must be a range value or an array literal (suggestion: use '$suggestion' here)")
+ else
+ valueerr("array initialization value must be a range value or an array literal")
+ }
+ }
+
val length = decl.arraysize?.constIndex()
if(length==null)
err("array length must be known at compile-time")
@@ -1831,14 +1851,7 @@ internal class AstChecker(private val program: Program,
DataType.UWORD -> sourceDatatype == DataType.UBYTE || sourceDatatype == DataType.UWORD
DataType.FLOAT -> sourceDatatype in NumericDatatypes
DataType.STR -> sourceDatatype == DataType.STR
- else -> {
- if(targetDatatype in ArrayDatatypes && sourceValue is ArrayLiteral)
- true // assigning array literal to an array variable is allowed, size and type are checked elsewhere
- else {
- errors.err("cannot assign this value to variable of type $targetDatatype", position)
- false
- }
- }
+ else -> targetDatatype in ArrayDatatypes && sourceValue is ArrayLiteral
}
if(result)
diff --git a/compiler/test/ast/TestAstChecks.kt b/compiler/test/ast/TestAstChecks.kt
index ba5f553d1..e2847134f 100644
--- a/compiler/test/ast/TestAstChecks.kt
+++ b/compiler/test/ast/TestAstChecks.kt
@@ -104,7 +104,7 @@ class TestAstChecks: FunSpec({
val text = """
main {
sub start() {
- const ubyte[5] a = 5
+ const ubyte[5] a = [1,2,3,4,5]
a[2]=42
}
}
@@ -133,22 +133,6 @@ class TestAstChecks: FunSpec({
errors.errors[0] shouldContain "indexing requires"
}
- test("array decl with expression as size can be initialized with a single value") {
- val text = """
- main {
- sub start() {
- const ubyte n = 40
- const ubyte half = n / 2
- ubyte[half] @shared a = 5
- }
- }
- """
- val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
- compileText(C64Target(), true, text, writeAssembly = true, errors=errors) shouldNotBe null
- errors.errors.size shouldBe 0
- errors.warnings.size shouldBe 0
- }
-
test("unicode in identifier names is working") {
val text = """
%import floats
diff --git a/compiler/test/codegeneration/TestVariables.kt b/compiler/test/codegeneration/TestVariables.kt
index 4af007a5e..6c143cbad 100644
--- a/compiler/test/codegeneration/TestVariables.kt
+++ b/compiler/test/codegeneration/TestVariables.kt
@@ -113,32 +113,4 @@ class TestVariables: FunSpec({
errors.errors[0] shouldContain "value has incompatible type"
errors.errors[1] shouldContain "value has incompatible type"
}
-
- test("initialization of boolean array with single value") {
- val text = """
- main {
- sub start() {
- bool[10] sieve0 = false
- bool[10] sieve1 = true
- sieve0[0] = true
- sieve1[0] = true
- }
- }
- """
- compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
- }
-
- test("initialization of boolean array with single value of wrong type fails") {
- val text = """
- main {
- sub start() {
- bool[10] sieve2 = 42
- }
- }
- """
- val errors = ErrorReporterForTests()
- compileText(C64Target(), false, text, writeAssembly = true, errors=errors) shouldBe null
- errors.errors.size shouldBe 1
- errors.errors[0] shouldContain "initializer value is not a boolean"
- }
})
diff --git a/docs/source/programming.rst b/docs/source/programming.rst
index e2fdc0b05..15f831d9b 100644
--- a/docs/source/programming.rst
+++ b/docs/source/programming.rst
@@ -290,7 +290,7 @@ always have to be constants. Here are some examples of arrays::
byte[10] array ; array of 10 bytes, initially set to 0
byte[] array = [1, 2, 3, 4] ; initialize the array, size taken from value
- ubyte[99] array = 255 ; initialize array with 99 times 255 [255, 255, 255, 255, ...]
+ ubyte[99] array = [255]*99 ; initialize array with 99 times 255 [255, 255, 255, 255, ...]
byte[] array = 100 to 199 ; initialize array with [100, 101, ..., 198, 199]
str[] names = ["ally", "pete"] ; array of string pointers/addresses (equivalent to array of uwords)
uword[] others = [names, array] ; array of pointers/addresses to other arrays
@@ -306,7 +306,8 @@ always have to be constants. Here are some examples of arrays::
This means byte arrays should be <= 256 elements, word arrays <= 128 elements (256 if
it's a split array - see below), and float arrays <= 51 elements.
-You can write out an array initializer list over several lines if you want to improve readability.
+Arrays can be initialized with a range expression or an array literal value.
+You can write out such an initializer value over several lines if you want to improve readability.
Note that the various keywords for the data type and variable type (``byte``, ``word``, ``const``, etc.)
can't be used as *identifiers* elsewhere. You can't make a variable, block or subroutine with the name ``byte``
diff --git a/docs/source/syntaxreference.rst b/docs/source/syntaxreference.rst
index 941504f30..12ae61f64 100644
--- a/docs/source/syntaxreference.rst
+++ b/docs/source/syntaxreference.rst
@@ -363,7 +363,7 @@ Various examples::
bool flag = true
byte[] values = [11, 22, 33, 44, 55]
byte[5] values ; array of 5 bytes, initially set to zero
- byte[5] values = 255 ; initialize with five 255 bytes
+ byte[5] values = [255]*5 ; initialize with five 255 bytes
word @zp zpword = 9999 ; prioritize this when selecting vars for zeropage storage
uword @requirezp zpaddr = $3000 ; we require this variable in zeropage
@@ -403,8 +403,11 @@ type identifier type storage size example var declara
implicitly terminated by a 0-byte
=============== ======================= ================= =========================================
-**arrays:** you can split an array initializer list over several lines if you want. When an initialization
-value is given, the array size in the declaration can be omitted.
+**arrays:**
+Arrays can be initialized with a range expression or an array literal value.
+You can write out such an initializer value over several lines if you want to improve readability.
+When an initialization value is given, you are allowed to omit the array size in the declaration,
+because it can be inferred from the initialization value.
**numbers:** unless prefixed for hex or binary as described below, all numbers are decimal numbers. There is no octal notation.
@@ -542,9 +545,10 @@ memory at the given index (and allows index values of word size). See :ref:`poin
String
^^^^^^
A string literal can occur with or without an encoding prefix (encoding followed by ':' followed by the string itself).
+String length is limited to 255 characters.
+You can use '+' and '*' to concatenate or repeat string fragments to build up a larger string literal.
When this is omitted, the string is stored in the machine's default character encoding (which is PETSCII on the CBM machines).
You can choose to store the string in other encodings such as ``sc`` (screencodes) or ``iso`` (iso-8859-15).
-String length is limited to 255 characters.
Here are examples of the various encodings:
- ``"hello"`` a string translated into the default character encoding (PETSCII on the CBM machines)
diff --git a/docs/source/todo.rst b/docs/source/todo.rst
index e9fd9dbf7..9a03d3f85 100644
--- a/docs/source/todo.rst
+++ b/docs/source/todo.rst
@@ -1,6 +1,12 @@
TODO
====
+- remove support for array variable initialization with a single value, just require explicitly creating the value array [42] * 10 (which is what the compiler now does for you implicitly)
+- should the array-to-array assignment support be removed and instead require an explicit copy function call? What prog8_lib_arraycopy() now does. Or just use memcopy.
+- should we add a cleararray builtin function that can efficiently set every element in the array to the given value
+
+
+
Improve register load order in subroutine call args assignments:
in certain situations, the "wrong" order of evaluation of function call arguments is done which results
in overwriting registers that already got their value, which requires a lot of stack juggling (especially on plain 6502 cpu!)
@@ -9,11 +15,6 @@ Maybe this routine can be made more intelligent. See usesOtherRegistersWhileEva
Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^
-
-- remove support for array variable initialization with a single value, just require explicitly creating the value array [42] * 10 (which is what the compiler now does for you implicitly)
-- should the array-to-array assignment support be removed and instead require an explicit copy function call? What prog8_lib_arraycopy() now does. Or just use memcopy.
-- should we add a cleararray builtin function that can efficiently set every element in the array to the given value
-
- improve detection that a variable is not read before being written so that initializing it to zero can be omitted (only happens now if a vardecl is immediately followed by a for loop for instance)
- Improve the SublimeText syntax file for prog8, you can also install this for 'bat': https://github.com/sharkdp/bat?tab=readme-ov-file#adding-new-syntaxes--language-definitions
- Can we support signed % (remainder) somehow?
diff --git a/examples/cube3d-float.p8 b/examples/cube3d-float.p8
index 071ca8285..641cdf96c 100644
--- a/examples/cube3d-float.p8
+++ b/examples/cube3d-float.p8
@@ -12,9 +12,9 @@ main {
float[] zcoor = [ -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0 ]
; storage for rotated coordinates
- float[len(xcoor)] rotatedx=0.0
- float[len(ycoor)] rotatedy=0.0
- float[len(zcoor)] rotatedz=-1.0
+ float[len(xcoor)] rotatedx
+ float[len(ycoor)] rotatedy
+ float[len(zcoor)] rotatedz
sub start() {
float time=0.0
diff --git a/examples/test.p8 b/examples/test.p8
index af54f1ce6..0622fd138 100644
--- a/examples/test.p8
+++ b/examples/test.p8
@@ -1,42 +1,63 @@
-%import monogfx
+%import floats
%import textio
-%import math
%option no_sysinit
%zeropage basicsafe
-
main {
+ str name = "xyz" * 3
+ bool[3] boolarray = true
+ ubyte[3] bytearray = 52
+ uword[3] wordarray = 5544
+ float[3] floatarray = 123.45
+
+ ubyte[3] bytearray2 = 10 to 12
+ uword[3] wordarray2 = 5540 to 5542
+ float[3] floatarray2 = 123 to 125
+
+ bool[3] boolarray3
+ ubyte[3] bytearray3
+ uword[3] wordarray3
+ float[3] floatarray3
+
sub start() {
- monogfx.lores()
- demofill()
- }
-
- sub demofill() {
- const uword offsetx = 0
- const uword offsety = 0
-
- monogfx.circle(offsetx+160, offsety+120, 110, true)
- monogfx.rect(offsetx+180, offsety+5, 25, 190, true)
- monogfx.line(offsetx+100, offsety+150, offsetx+240, offsety+10, true)
- monogfx.line(offsetx+101, offsety+150, offsetx+241, offsety+10, true)
- monogfx.rect(offsetx+150, offsety+130, 10, 100, true)
-
- sys.wait(30)
-
- cbm.SETTIM(0,0,0)
- monogfx.fill(offsetx+100,offsety+100,true)
- monogfx.fill(offsetx+100,offsety+100,false)
- uword duration = cbm.RDTIM16()
- sys.wait(30)
-
- monogfx.textmode()
+ txt.print(name)
+ txt.nl()
+ for cx16.r1L in 0 to 2 {
+ txt.print_bool(boolarray[cx16.r1L])
+ txt.spc()
+ txt.print_ub(bytearray[cx16.r1L])
+ txt.spc()
+ txt.print_uw(wordarray[cx16.r1L])
+ txt.spc()
+ floats.print(floatarray[cx16.r1L])
+ txt.nl()
+ }
+ txt.nl()
txt.nl()
- txt.print_uw(duration)
- txt.print(" jiffies\n")
- ; before optimizations: ~166 jiffies
+ for cx16.r1L in 0 to 2 {
+ txt.print_ub(bytearray2[cx16.r1L])
+ txt.spc()
+ txt.print_uw(wordarray2[cx16.r1L])
+ txt.spc()
+ floats.print(floatarray2[cx16.r1L])
+ txt.nl()
+ }
+ txt.nl()
+ txt.nl()
+ for cx16.r1L in 0 to 2 {
+ txt.print_bool(boolarray3[cx16.r1L])
+ txt.spc()
+ txt.print_ub(bytearray3[cx16.r1L])
+ txt.spc()
+ txt.print_uw(wordarray3[cx16.r1L])
+ txt.spc()
+ floats.print(floatarray3[cx16.r1L])
+ txt.nl()
+ }
+ txt.nl()
}
}
diff --git a/gradle.properties b/gradle.properties
index 69cfb6a64..d51a58952 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -4,5 +4,5 @@ org.gradle.parallel=true
org.gradle.daemon=true
kotlin.code.style=official
javaVersion=11
-kotlinVersion=2.0.20
-version=10.4.2
+kotlinVersion=2.0.21
+version=10.5-SNAPSHOT