diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AsmAssignment.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AsmAssignment.kt index c140b9e13..8c39eb101 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AsmAssignment.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AsmAssignment.kt @@ -10,8 +10,8 @@ internal enum class TargetStorageKind { ARRAY, MEMORY, REGISTER, - POINTER, // wherever the pointer variable points to - VOID // assign nothing - used in multi-value assigns for void placeholders + POINTER, + VOID // assign nothing - used in multi-value assigns for void placeholders } internal enum class SourceStorageKind { @@ -91,7 +91,6 @@ internal class AsmAssignTarget(val kind: TargetStorageKind, array != null -> return AsmAssignTarget(TargetStorageKind.ARRAY, asmgen, type, definingSub, target.position, array = array, origAstTarget = this) memory != null -> return AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, type, definingSub, target.position, memory = memory, origAstTarget = this) pointerDeref != null -> return AsmAssignTarget(TargetStorageKind.POINTER, asmgen, type, definingSub, target.position, pointer = pointerDeref, origAstTarget = this) - indexedPointerDeref != null -> TODO("assign to indexed pointer ${target.position} - for now, split up the assignment target using a temporary pointer variable") else -> throw AssemblyError("weird target") } } diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt index ac515a868..296e66688 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt @@ -596,9 +596,6 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express return result } - indexedPointerDeref != null -> { - TODO("assign to indexed pointer ${assignment.target.position} - for now, split up the assignment target using a temporary pointer variable") - } else -> { throw AssemblyError("weird assigntarget") } diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt index abf3af4ec..77de35bb1 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt @@ -88,7 +88,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { is PtFunctionCall -> translate(expr) is PtContainmentCheck -> translate(expr) is PtPointerDeref -> translate(expr) - is PtArrayIndexedPointerDeref -> TODO("translate array indexed pointer deref expression $expr") is PtRange, is PtArray, is PtString -> throw AssemblyError("range/arrayliteral/string should no longer occur as expression") diff --git a/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt b/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt index b3922fd52..a26bc8fa8 100644 --- a/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt +++ b/compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt @@ -662,6 +662,61 @@ _after: override fun after(deref: ArrayIndexedPtrDereference, parent: Node): Iterable { // get rid of the ArrayIndexedPtrDereference AST node, replace it with other AST nodes that are equivalent + fun pokeFunc(dt: DataType): Pair { + return when { + dt.isBool -> "pokebool" to null + dt.isUnsignedByte -> "poke" to null + dt.isSignedByte -> "poke" to DataType.UBYTE + dt.isUnsignedWord -> "pokew" to null + dt.isSignedWord -> "pokew" to DataType.UWORD + dt.isLong -> "pokel" to null + dt.isFloat -> "pokef" to null + else -> throw FatalAstException("can only deref a numeric or boolean pointer here") + } + } + + if(parent is AssignTarget) { + if(!deref.derefLast) { + val assignment = parent.parent as Assignment + val field = deref.chain.last() + val ptr = deref.chain.dropLast(1) + if(field.second==null && ptr.last().second!=null) { + val ptrName = ptr.map { it.first } + val ptrVar = deref.definingScope.lookup(ptrName) as? VarDecl + if(ptrVar!=null && (ptrVar.datatype.isPointer || ptrVar.datatype.isPointerArray)) { + val struct = ptrVar.datatype.subType!! as StructDecl + val offsetNumber = NumericLiteral.optimalInteger(struct.offsetof(field.first, program.memsizer)!!.toInt(), deref.position) + val pointerIdentifier = IdentifierReference(ptrName, deref.position) + val address: Expression + if(ptrVar.datatype.isPointer) { + // pointer[idx].field = value --> pokeXXX(pointer as uword + idx*sizeof(Struct) + offsetof(Struct.field), value) + val structSize = ptrVar.datatype.dereference().size(program.memsizer) + val pointerAsUword = TypecastExpression(pointerIdentifier, DataType.UWORD, true, deref.position) + val idx = ptr.last().second!!.indexExpr + val scaledIndex = BinaryExpression(idx, "*", NumericLiteral(BaseDataType.UWORD, structSize.toDouble(), deref.position), deref.position) + val structAddr = BinaryExpression(pointerAsUword, "+", scaledIndex, deref.position) + address = BinaryExpression(structAddr, "+", offsetNumber, deref.position) + } + else { + // pointerarray[idx].field = value --> pokeXXX(pointerarray[idx] as uword + offsetof(Struct.field), value) + val index = ArrayIndexedExpression(pointerIdentifier, null, ptr.last().second!!, deref.position) + val pointerAsUword = TypecastExpression(index, DataType.UWORD, true, deref.position) + address = BinaryExpression(pointerAsUword, "+", offsetNumber, deref.position) + } + val (pokeFunc, valueCast) = pokeFunc(parent.inferType(program).getOrUndef()) + val value = if(valueCast==null) assignment.value else TypecastExpression(assignment.value, valueCast, true, assignment.value.position) + val pokeCall = FunctionCallStatement(IdentifierReference(listOf(pokeFunc), assignment.position), + mutableListOf(address, value), false, assignment.position) + + if(assignment.isAugmentable) + errors.warn("in-place assignment of indexed pointer variable currently is very inefficient, maybe use a temporary pointer variable", assignment.position) + return listOf(IAstModification.ReplaceNode(assignment, pokeCall, assignment.parent)) + } + } + } + } + + if(deref.chain.last().second!=null && deref.derefLast && deref.chain.dropLast(1).all { it.second==null } ) { // parent could be Assigment directly, or a binexpr chained pointer expression (with '.' operator) @@ -714,15 +769,7 @@ _after: val dt = deref.inferType(program).getOrUndef() if(dt.isNumericOrBool) { // if it's something else beside number (like, a struct instance) we don't support rewriting that... - val (pokeFunc, cast) = - if (dt.isBool) "pokebool" to null - else if (dt.isUnsignedByte) "poke" to null - else if (dt.isSignedByte) "poke" to DataType.UBYTE - else if (dt.isUnsignedWord) "pokew" to null - else if (dt.isSignedWord) "pokew" to DataType.UWORD - else if (dt.isLong) "pokel" to null - else if (dt.isFloat) "pokef" to null - else throw FatalAstException("can only deref a numeric or boolean pointer here") + val (pokeFunc, cast) = pokeFunc(dt) val indexer = deref.chain.last().second!! val identifier = IdentifierReference(deref.chain.map { it.first }, deref.position) val indexed = ArrayIndexedExpression(identifier, null, indexer, deref.position) diff --git a/compiler/src/prog8/compiler/astprocessing/SimplifiedAstMaker.kt b/compiler/src/prog8/compiler/astprocessing/SimplifiedAstMaker.kt index 6c13f585a..0721a32d9 100644 --- a/compiler/src/prog8/compiler/astprocessing/SimplifiedAstMaker.kt +++ b/compiler/src/prog8/compiler/astprocessing/SimplifiedAstMaker.kt @@ -107,20 +107,6 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro } } - private fun transform(arrayIndexedDereference: ArrayIndexedPtrDereference): PtArrayIndexedPointerDeref { - val type = arrayIndexedDereference.inferType(program).getOrElse { - throw FatalAstException("unknown dt") - } - val chain = arrayIndexedDereference.chain.map { - if(it.second==null) - it.first to null - else - it.first to transformExpression(it.second!!.indexExpr) - } - return PtArrayIndexedPointerDeref(type, chain, arrayIndexedDereference.derefLast, arrayIndexedDereference.position) - } - - private fun transform(deref: PtrDereference): PtPointerDeref { val type = deref.inferType(program).getOrElse { throw FatalAstException("unknown dt") @@ -284,7 +270,7 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro srcTarget.arrayindexed!=null -> target.add(transform(srcTarget.arrayindexed!!)) srcTarget.memoryAddress!=null -> target.add(transform(srcTarget.memoryAddress!!)) srcTarget.pointerDereference!=null -> target.add(transform(srcTarget.pointerDereference!!)) - srcTarget.arrayIndexedDereference!=null -> target.add(transform(srcTarget.arrayIndexedDereference!!)) + srcTarget.arrayIndexedDereference!=null -> throw FatalAstException("this should have been converted to some other ast nodes ${srcTarget.position}") !srcTarget.void -> throw FatalAstException("invalid AssignTarget") } return target diff --git a/compiler/test/TestPointers.kt b/compiler/test/TestPointers.kt index 07c9602f8..c71b2fdf2 100644 --- a/compiler/test/TestPointers.kt +++ b/compiler/test/TestPointers.kt @@ -3,7 +3,6 @@ package prog8tests.compiler import io.kotest.assertions.withClue import io.kotest.core.spec.style.FunSpec import io.kotest.engine.spec.tempdir -import io.kotest.matchers.comparables.shouldBeGreaterThanOrEqualTo import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe import io.kotest.matchers.string.shouldContain @@ -1346,31 +1345,32 @@ other { compileText(Cx16Target(), false, src, outputDir) shouldNotBe null } - test("a.b.c[i]^^.value = X where pointer is struct gives good error message") { + test("support for assigning to indexed pointers") { val src=""" main { + sub start() { - other.foo.listarray[3]^^.value = cx16.r0 - other.foo() - } -} + sprptr[2]^^.y = 99 + pokew(sprptr as uword + (sizeof(Sprite) as uword)*2 + offsetof(Sprite.y), 99) + sprptr[cx16.r0L]^^.y = 99 + pokew(sprptr as uword + (sizeof(Sprite) as uword)*cx16.r0L + offsetof(Sprite.y), 99) -other { - sub foo() { - struct List { - bool b - uword value - } - - ^^List[10] listarray - listarray[3]^^.value = cx16.r0 - listarray[3]^^ = 999 ; cannot assign word value to struct instance + sprites[2]^^.y = 99 + pokew(sprites[2] as uword + offsetof(Sprite.y), 99) + sprites[cx16.r0L]^^.y = 99 + pokew(sprites[cx16.r0L] as uword + offsetof(Sprite.y), 99) } + + struct Sprite { + ubyte x + uword y + } + + ^^Sprite[4] @shared sprites + ^^Sprite @shared sprptr }""" - val errors = ErrorReporterForTests() - compileText(VMTarget(), false, src, outputDir, errors=errors) shouldBe null - errors.errors.size shouldBeGreaterThanOrEqualTo 1 - errors.errors[0] shouldContain "assigning this value to struct instance not supported" + compileText(VMTarget(), false, src, outputDir) shouldNotBe null + compileText(C64Target(), false, src, outputDir) shouldNotBe null } xtest("array indexed assignment parses with and without explicit dereference after struct pointer [IGNORED because it's a parser error right now]") { diff --git a/docs/source/structpointers.rst b/docs/source/structpointers.rst index 05a8fb88e..c49e0af87 100644 --- a/docs/source/structpointers.rst +++ b/docs/source/structpointers.rst @@ -15,25 +15,26 @@ Structs and Pointers priority over other variables to be placed into zeropage. .. note:: - Due to some limitations in the language parser, not all pointer related syntax is currently supported - if it is a pointer to a struct type. + Due to a few limitations in the language parser, some pointer related syntax is currently unsupported. The compiler tries its best to give a descriptive error message but sometimes there is still a - parser limitation that has to be worked around at the moment. For example, this pointer arithmetic - indexing syntax is not supported right now *to assign to* and will result in a parse error (note that - using it as an expression value does work correctly):: + parser limitation that has to be worked around at the moment. For example, this assigment syntax doesn't parse correctly:: ^^Node np - np[2].field = 9999 ; cannot assign to this yet - ubyte value = np[2].field ; this does work though. + np[2].field = 9999 ; cannot use this syntax as assignment target right now + ubyte value = np[2].field ; note that using it as expression value works fine - To work around this (and similar) cases you'll have to break up the expression in multiple steps, - in this case something like:: + To work around this you'll have to explicitly write the pointer dereferencing operator, + or break up the expression in multiple steps (which can be beneficial too when you are assigning multiple fields + because it will save a pointer calculation for every assignment):: + ^^Node np + np[2]^^.field = 9999 + + ; alternatively, split up: ^^Node thenode = &&np[2] thenode.field = 9999 - Legacy untyped pointers (uword) ------------------------------- diff --git a/docs/source/todo.rst b/docs/source/todo.rst index e258c759f..80e50fea3 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -5,10 +5,10 @@ TODO STRUCTS and TYPED POINTERS -------------------------- -- make this array indexed assignment work: ^^Node np / np[2]^^.field = 9999 (same for pointer arrays!) likely needs more support in the assignment target class (remove Note in docs when fixed) - implement the remaining TODO's in PointerAssignmentsGen. - optimize deref in PointerAssignmentsGen: optimize 'forceTemporary' to only use a temporary when the offset is >0 - optimize the float copying in assignIndexedPointer() (also word?) +- optimize augmented assignments to indexed pointer targets like sprptr[2]^^.y++ (these are now not performend in-place but as a regular assignment) - implement even more struct instance assignments (via memcopy) in CodeDesugarer (see the TODO) (add to documentation as well, paragraph 'Structs') - support @nosplit pointer arrays? - support pointer to pointer? diff --git a/examples/test.p8 b/examples/test.p8 index 385496569..de2890de7 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,19 +1,14 @@ main { sub start() { - ; TODO assigning to pointer indexed is not yet supported: - sprptr[2]^^.y = 99 - sprptr[cx16.r0L]^^.y = 99 - sprites[2]^^.y = 99 - sprites[cx16.r0L]^^.y = 99 + sprptr[2]^^.y++ } struct Sprite { - uword x - ubyte y + ubyte x + uword y } - ^^Sprite[4] @shared sprites ^^Sprite @shared sprptr } diff --git a/simpleAst/src/prog8/code/ast/AstExpressions.kt b/simpleAst/src/prog8/code/ast/AstExpressions.kt index 1f1026e65..965246b47 100644 --- a/simpleAst/src/prog8/code/ast/AstExpressions.kt +++ b/simpleAst/src/prog8/code/ast/AstExpressions.kt @@ -139,7 +139,6 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit is PtRange -> true is PtString -> true is PtPointerDeref -> false - is PtArrayIndexedPointerDeref -> false is PtTypeCast -> value.isSimple() is PtIfExpression -> condition.isSimple() && truevalue.isSimple() && falsevalue.isSimple() is PtBranchCondExpression -> truevalue.isSimple() && falsevalue.isSimple() @@ -453,15 +452,6 @@ class PtPointerDeref(type: DataType, val chain: List, val derefLast: Boo } } -class PtArrayIndexedPointerDeref( - type: DataType, - val chain: List>, - val derefLast: Boolean, - position: Position -) : PtExpression(type, position) { - -} - // special node that isn't created from compiling user code, but used internally in the Intermediate Code class PtIrRegister(val register: Int, type: DataType, position: Position) : PtExpression(type, position) diff --git a/simpleAst/src/prog8/code/ast/AstPrinter.kt b/simpleAst/src/prog8/code/ast/AstPrinter.kt index 1f0a3377d..19bb14e49 100644 --- a/simpleAst/src/prog8/code/ast/AstPrinter.kt +++ b/simpleAst/src/prog8/code/ast/AstPrinter.kt @@ -198,20 +198,6 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni val deref = if(node.derefLast) "^^" else "" "deref $chain $deref ${type(node.type)}" } - is PtArrayIndexedPointerDeref -> { - buildString { - for(c in node.chain) { - append(c.first) - c.second?.let { - append("[${txt(it)}]") - } - if(c !== node.chain.last()) - append("^^.") - } - if(node.derefLast) - append("^^") - } - } } } diff --git a/simpleAst/src/prog8/code/ast/AstStatements.kt b/simpleAst/src/prog8/code/ast/AstStatements.kt index a79b81307..edc806aef 100644 --- a/simpleAst/src/prog8/code/ast/AstStatements.kt +++ b/simpleAst/src/prog8/code/ast/AstStatements.kt @@ -124,8 +124,6 @@ class PtAssignTarget(val void: Boolean, position: Position) : PtNode(position) { get() = children.single() as? PtMemoryByte val pointerDeref: PtPointerDeref? get() = children.single() as? PtPointerDeref - val indexedPointerDeref: PtArrayIndexedPointerDeref? - get() = children.single() as? PtArrayIndexedPointerDeref val type: DataType get() { @@ -134,7 +132,6 @@ class PtAssignTarget(val void: Boolean, position: Position) : PtNode(position) { is PtArrayIndexer -> tgt.type is PtMemoryByte -> tgt.type is PtPointerDeref -> tgt.type - is PtArrayIndexedPointerDeref -> tgt.type else -> throw AssemblyError("weird target $tgt") } }