allow Python-style negative array indexing to count from the end

This commit is contained in:
Irmen de Jong 2023-12-23 16:37:28 +01:00
parent ad9eaeafeb
commit 4cd9bb8f99
7 changed files with 42 additions and 16 deletions

View File

@ -170,7 +170,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
if(arrayIx.usesPointerVariable) {
if(eltSize!=1)
throw AssemblyError("non-array var indexing requires bytes dt")
if(arrayIx.index.type!=DataType.UBYTE)
if(arrayIx.index.type !in ByteDatatypes)
throw AssemblyError("non-array var indexing requires bytes index")
val tr = translateExpression(arrayIx.index)
addToResult(result, tr, tr.resultReg, -1)

View File

@ -1374,11 +1374,10 @@ internal class AstChecker(private val program: Program,
if(target.datatype !in IterableDatatypes && target.datatype!=DataType.UWORD)
errors.err("indexing requires an iterable or address uword variable", arrayIndexedExpression.position)
val arraysize = target.arraysize?.constIndex()
val index = arrayIndexedExpression.indexer.constIndex()
if(arraysize!=null) {
// check out of bounds
val index = arrayIndexedExpression.indexer.constIndex()
if(index!=null && (index<0 || index>=arraysize))
errors.err("array index out of bounds", arrayIndexedExpression.indexer.position)
errors.err("index out of bounds", arrayIndexedExpression.indexer.position)
} else if(target.datatype == DataType.STR) {
if(target.value is StringLiteral) {
// check string lengths for non-memory mapped strings
@ -1387,6 +1386,8 @@ internal class AstChecker(private val program: Program,
if (index != null && (index < 0 || index >= stringLen))
errors.err("index out of bounds", arrayIndexedExpression.indexer.position)
}
} else if(index!=null && index<0) {
errors.err("index out of bounds", arrayIndexedExpression.indexer.position)
}
} else
errors.err("indexing requires a variable to act upon", arrayIndexedExpression.position)

View File

@ -262,5 +262,20 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
}
return noModifications
}
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
val index = arrayIndexedExpression.indexer.constIndex()
if(index!=null && index<0) {
val target = arrayIndexedExpression.arrayvar.targetVarDecl(program)
val arraysize = target?.arraysize?.constIndex()
if(arraysize!=null) {
// replace the negative index by the normal index
val newIndex = NumericLiteral.optimalNumeric(arraysize+index, arrayIndexedExpression.indexer.position)
arrayIndexedExpression.indexer.indexExpr = newIndex
newIndex.linkParents(arrayIndexedExpression.indexer)
}
}
return noModifications
}
}

View File

@ -294,6 +294,7 @@ Here are some examples of arrays::
value = array[3] ; the fourth value in the array (index is 0-based)
char = string[4] ; the fifth character (=byte) in the string
char = string[-2] ; the second-to-last character in the string (Python-style indexing from the end)
.. note::
Right now, the array should be small enough to be indexable by a single byte index.
@ -329,6 +330,7 @@ An uword variable can be used in limited scenarios as a 'pointer' to a byte in m
dynamic, location. You can use array indexing on a pointer variable to use it as a byte array at
a dynamic location in memory: currently this is equivalent to directly referencing the bytes in
memory at the given index. In contrast to a real array variable, the index value can be the size of a word.
Unlike array variables, you cannot use a negative index to count from the end, because the size of the array is unknown.
See also :ref:`pointervars_programming`
**LSB/MSB split word arrays:**

View File

@ -507,10 +507,13 @@ Array indexing
^^^^^^^^^^^^^^
Strings and arrays are a sequence of values. You can access the individual values by indexing.
Syntax is familiar with brackets: ``arrayvar[x]`` ::
Negative index means counted from the end of the array rather than the beginning, where -1 means
the last element in the array, -2 the second-to-last, etc. (Python uses this same scheme)
Use brackets to index into an array: ``arrayvar[x]`` ::
array[2] ; the third byte in the array (index is 0-based)
string[4] ; the fifth character (=byte) in the string
array[-2] ; the second-to-last element
Note: you can also use array indexing on a 'pointer variable', which is basically an uword variable
containing a memory address. Currently this is equivalent to directly referencing the bytes in

View File

@ -84,4 +84,3 @@ Other language/syntax features to think about
challenges: how to not make this too X16 specific? How does the compiler know what bank to switch (ram/rom)?
How to make it performant when we want to (i.e. NOT have it use callfar/auto bank switching) ?
- chained comparisons `10<x<20` , `x==y==z` (desugars to `10<x and x<20`, `x==y and y==z`) BUT this changes the semantics of what it is right now ! (x==(y==z) --> x==true)
- negative array index to refer to an element from the end of the array. Python `[-1]` or Raku syntax `[\*-1]` , `[\*/2]` .... \*=size of the array

View File

@ -3,16 +3,22 @@
main {
sub start() {
uword function = &test
uword @shared derp = call(function)
txt.print_uw(derp)
txt.nl()
void call(function)
}
ubyte[] barr = [1,2,3,4,5,6,7,8,9]
uword[] warr = [111,222,333,444,555,666,777,888,999]
uword pointer = &barr
byte index = 2
sub test() -> uword {
txt.print("test\n")
cx16.r0++
return 999
txt.print_ub(barr[7])
txt.nl()
txt.print_ub(barr[-2])
txt.nl()
txt.print_ub(pointer[7])
txt.nl()
txt.print_ub(pointer[index])
txt.nl()
txt.print_uw(warr[7])
txt.nl()
txt.print_uw(warr[-2])
txt.nl()
}
}