diff --git a/compiler/src/prog8/ast/expressions/AstExpressions.kt b/compiler/src/prog8/ast/expressions/AstExpressions.kt index 53ac79a31..2ddfd5e39 100644 --- a/compiler/src/prog8/ast/expressions/AstExpressions.kt +++ b/compiler/src/prog8/ast/expressions/AstExpressions.kt @@ -569,6 +569,7 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be val dts = datatypesInArray.map { it.typeOrElse(DataType.STRUCT) } return when { DataType.FLOAT in dts -> InferredTypes.InferredType.known(DataType.ARRAY_F) + DataType.STR in dts -> InferredTypes.InferredType.known(DataType.ARRAY_UW) DataType.WORD in dts -> InferredTypes.InferredType.known(DataType.ARRAY_W) DataType.UWORD in dts -> InferredTypes.InferredType.known(DataType.ARRAY_UW) DataType.BYTE in dts -> InferredTypes.InferredType.known(DataType.ARRAY_B) diff --git a/compiler/src/prog8/ast/processing/AstChecker.kt b/compiler/src/prog8/ast/processing/AstChecker.kt index e411eca76..38bcf9f19 100644 --- a/compiler/src/prog8/ast/processing/AstChecker.kt +++ b/compiler/src/prog8/ast/processing/AstChecker.kt @@ -742,7 +742,15 @@ internal class AstChecker(private val program: Program, checkValueTypeAndRangeArray(array.type.typeOrElse(DataType.STRUCT), null, arrayspec, array) } - if(!array.value.all { it is NumericLiteralValue || it is AddressOf || it is StringLiteralValue }) { + fun isStringElement(e: Expression): Boolean { + if(e is IdentifierReference) { + val decl = e.targetVarDecl(program.namespace)!! + return decl.datatype==DataType.STR + } + return e is StringLiteralValue + } + + if(!array.value.all { it is NumericLiteralValue || it is AddressOf || isStringElement(it) }) { // TODO for now, array literals have to consist of all compile time constant values... errors.err("array literal doesn't consist of only compile time constant values", array.position) } diff --git a/compiler/src/prog8/ast/processing/VerifyFunctionArgTypes.kt b/compiler/src/prog8/ast/processing/VerifyFunctionArgTypes.kt index 5bd69758e..3baf8d667 100644 --- a/compiler/src/prog8/ast/processing/VerifyFunctionArgTypes.kt +++ b/compiler/src/prog8/ast/processing/VerifyFunctionArgTypes.kt @@ -26,6 +26,19 @@ class VerifyFunctionArgTypes(val program: Program) : IAstVisitor { } companion object { + + private fun argTypeCompatible(argDt: DataType, paramDt: DataType): Boolean { + if(argDt==paramDt) + return true + + // there are some exceptions that are considered compatible, such as STR <> UWORD + if(argDt==DataType.STR && paramDt==DataType.UWORD || + argDt==DataType.UWORD && paramDt==DataType.STR) + return true + + return false + } + fun checkTypes(call: IFunctionCall, scope: INameScope, program: Program): String? { val argtypes = call.args.map { it.inferType(program).typeOrElse(DataType.STRUCT) } val target = call.target.targetStatement(scope) @@ -34,7 +47,7 @@ class VerifyFunctionArgTypes(val program: Program) : IAstVisitor { if(call.args.size != target.parameters.size) return "invalid number of arguments" val paramtypes = target.parameters.map { it.type } - val mismatch = argtypes.zip(paramtypes).indexOfFirst { it.first != it.second} + val mismatch = argtypes.zip(paramtypes).indexOfFirst { !argTypeCompatible(it.first, it.second) } if(mismatch>=0) { val actual = argtypes[mismatch].toString() val expected = paramtypes[mismatch].toString() @@ -47,7 +60,8 @@ class VerifyFunctionArgTypes(val program: Program) : IAstVisitor { return "invalid number of arguments" val paramtypes = func.parameters.map { it.possibleDatatypes } for (x in argtypes.zip(paramtypes).withIndex()) { - if (x.value.first !in x.value.second) { + val anyCompatible = x.value.second.any { argTypeCompatible(x.value.first, it) } + if (!anyCompatible) { val actual = x.value.first.toString() val expected = x.value.second.toString() return "argument ${x.index + 1} type mismatch, was: $actual expected: $expected" diff --git a/compiler/src/prog8/ast/statements/AstStatements.kt b/compiler/src/prog8/ast/statements/AstStatements.kt index a316af3b3..b5f9f440d 100644 --- a/compiler/src/prog8/ast/statements/AstStatements.kt +++ b/compiler/src/prog8/ast/statements/AstStatements.kt @@ -214,8 +214,9 @@ open class VarDecl(val type: VarDeclType, DataType.UWORD -> DataType.ARRAY_UW DataType.WORD -> DataType.ARRAY_W DataType.FLOAT -> DataType.ARRAY_F + DataType.STR -> DataType.ARRAY_UW // use memory address of the string instead else -> { - datatypeErrors.add(SyntaxError("array can only contain bytes/words/floats", position)) + datatypeErrors.add(SyntaxError("array can only contain bytes/words/floats/strings(ptrs)", position)) DataType.ARRAY_UB } } diff --git a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt index 4cfa3f1a1..53e6794cb 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt @@ -406,10 +406,17 @@ internal class AsmGen(private val program: Program, "$"+number.toString(16).padStart(2, '0') } DataType.ARRAY_UW -> array.map { - if(it is NumericLiteralValue) { - "$" + it.number.toInt().toString(16).padStart(4, '0') - } else { - (it as AddressOf).identifier.nameInSource.joinToString(".") + when (it) { + is NumericLiteralValue -> { + "$" + it.number.toInt().toString(16).padStart(4, '0') + } + is AddressOf -> { + it.identifier.nameInSource.joinToString(".") + } + is IdentifierReference -> { + it.nameInSource.joinToString(".") + } + else -> throw AssemblyError("weird array elt dt") } } else -> throw AssemblyError("invalid arraysize type") @@ -711,10 +718,40 @@ internal class AsmGen(private val program: Program, } else { expressionsAsmGen.translateExpression(index) - when(register) { - CpuRegister.A -> out(" inx | lda P8ESTACK_LO,x") - CpuRegister.X -> out(" inx | lda P8ESTACK_LO,x | tax") - CpuRegister.Y -> out(" inx | ldy P8ESTACK_LO,x") + when(elementDt) { + in ByteDatatypes -> { + when (register) { + CpuRegister.A -> out(" inx | lda P8ESTACK_LO,x") + CpuRegister.X -> out(" inx | lda P8ESTACK_LO,x | tax") + CpuRegister.Y -> out(" inx | ldy P8ESTACK_LO,x") + } + } + in WordDatatypes -> { + out(""" + inx + lda P8ESTACK_LO,x + asl a""") + when (register) { + CpuRegister.A -> {} + CpuRegister.X -> out(" tax") + CpuRegister.Y -> out(" tay") + } + } + DataType.FLOAT -> { + require(DataType.FLOAT.memorySize()==5) + out(""" + inx + lda P8ESTACK_LO,x + asl a + asl a + clc + adc P8ESTACK_LO,x""") + when (register) { + CpuRegister.A -> {} + CpuRegister.X -> out(" tax") + CpuRegister.Y -> out(" tay") + } + } } } } diff --git a/docs/source/programming.rst b/docs/source/programming.rst index daf8a89bd..842a7da98 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -236,12 +236,13 @@ The largest 5-byte MFLPT float that can be stored is: **1.7014118345e+38** (ne Arrays ^^^^^^ -Array types are also supported. They can be made of bytes, words or floats:: +Array types are also supported. They can be made of bytes, words or floats, and strings:: byte[10] array ; array of 10 bytes, initially set to 0 byte[] array = [1, 2, 3, 4] ; initialize the array, size taken from value byte[99] array = 255 ; 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 uword) value = array[3] ; the fourth value in the array (index is 0-based) char = string[4] ; the fifth character (=byte) in the string @@ -293,6 +294,12 @@ large enough to contain the new value:: string1 = "new value" +.. info:: + Strings and uwords (=memory address) can often be interchanged. + An array of strings is actually an array of uwords where every element is the memory + address of the string. You can pass a memory address to assembly functions + that require a string as an argument. + .. caution:: It's probably best to avoid changing strings after they've been created. This includes changing certain letters by index, or by assigning a new value, or by @@ -718,7 +725,7 @@ sort(array) floating point values. reverse(array) - Reverse the values in the array (in-place). Supports all data types including floats. + Reverse the values in the array (in-place). Can be used after sort() to sort an array in descending order. len(x) diff --git a/docs/source/syntaxreference.rst b/docs/source/syntaxreference.rst index 359f184b5..2c1a87ed3 100644 --- a/docs/source/syntaxreference.rst +++ b/docs/source/syntaxreference.rst @@ -281,6 +281,7 @@ type identifier type storage size example var declara ``word[]`` signed word array depends on value ``word[] myvar = [1, 2, 3, 4]`` ``uword[]`` unsigned word array depends on value ``uword[] myvar = [1, 2, 3, 4]`` ``float[]`` floating-point array depends on value ``float[] myvar = [1.1, 2.2, 3.3, 4.4]`` +``str[]`` array with string ptrs 2*x bytes + strs ``str[] names = ["ally", "pete"]`` ``str`` string (petscii) varies ``str myvar = "hello."`` implicitly terminated by a 0-byte =============== ======================= ================= ========================================= diff --git a/examples/test.p8 b/examples/test.p8 index fa6bfc702..939d6921d 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -4,8 +4,28 @@ main { + + str[] names = ["aap", "noot", "mies", "vuur"] + uword[] names3 = ["aap", "noot", "mies", "vuur"] + ubyte[] values = [11,22,33,44] + sub start() { - txt.print("hello\n") + uword s + for s in names { + txt.print(s) + txt.chrout('\n') + } + txt.chrout('\n') + + txt.print(names[2]) + txt.chrout('\n') + txt.print(names[3]) + txt.chrout('\n') + + repeat { + txt.print(names3[rnd()&3]) ; TODO doesn't show correct names? only shows 'aap' and 'noot' works fine if idx in separate var + txt.chrout(' ') + } } }