cleaning up pointer indexing

This commit is contained in:
Irmen de Jong
2025-05-25 02:56:32 +02:00
parent 51269257ea
commit aaa81210ce
26 changed files with 382 additions and 417 deletions

View File

@@ -409,7 +409,6 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val targetMemory = assignment.target.memory
val targetArray = assignment.target.array
val targetPointerDeref = assignment.target.pointerDeref
val targetPointerIndexedDeref = assignment.target.pointerIndexedDeref
val valueDt = irType(assignment.value.type)
val targetDt = irType(assignment.target.type)
val result = mutableListOf<IRCodeChunkBase>()
@@ -604,9 +603,6 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
codeGen.storeValueAtPointersLocation(result, addressReg, targetPointerDeref.type, zero, actualValueReg)
return result
}
else if(targetPointerIndexedDeref!=null) {
TODO("assign to pointer indexed $targetPointerIndexedDeref")
}
else
throw AssemblyError("weird assigntarget")
}

View File

@@ -87,70 +87,12 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
is PtFunctionCall -> translate(expr)
is PtContainmentCheck -> translate(expr)
is PtPointerDeref -> translate(expr)
is PtPointerIndexedDeref -> translate(expr)
is PtRange,
is PtArray,
is PtString -> throw AssemblyError("range/arrayliteral/string should no longer occur as expression")
}
}
private fun translate(idxderef: PtPointerIndexedDeref): ExpressionCodeResult {
if (idxderef.type.isStructInstance)
throw AssemblyError("cannot translate POINTER[x] resulting in a struct instance (only when it results in a basic type); this is likely part of a larger expression POINTER[x].field and that has to be translated earlier as a whole")
val eltSize = codeGen.program.memsizer.memorySize(idxderef.type, null)
val result = mutableListOf<IRCodeChunkBase>()
if(!idxderef.variable.type.isPointer) {
TODO("expression: indexing non-pointer field ${idxderef.variable}")
}
TODO("expression: evaluate address of pointer dereference ${idxderef.position}")
val pointerReg = -1 // pointerTr.resultReg
val constIndex = idxderef.index.asConstInteger()
if(constIndex!=null) {
val offset = constIndex * eltSize
addInstr(result, IRInstruction(Opcode.ADD, IRDataType.WORD, reg1 = pointerReg, immediate = offset), null)
} else {
val indexTr = translateExpression(idxderef.index)
result += indexTr.chunks
result += IRCodeChunk(null, null).also {
val indexReg: Int
if (idxderef.index.type.isByte) {
// extend array index to word
indexReg = codeGen.registers.next(IRDataType.WORD)
it += IRInstruction(Opcode.EXT, IRDataType.BYTE, indexReg, indexTr.resultReg)
} else {
indexReg = indexTr.resultReg
}
it += codeGen.multiplyByConst(DataType.UWORD, indexReg, eltSize)
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1 = pointerReg, reg2 = indexReg)
}
}
when {
idxderef.type.isByteOrBool -> {
val resultReg = codeGen.registers.next(IRDataType.BYTE)
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.BYTE, reg1 = resultReg, reg2 = pointerReg), null)
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
}
idxderef.type.isWord || idxderef.type.isPointer -> {
// LOADI has an exception to allow reg1 and reg2 to be the same, so we can avoid using extra temporary registers and LOADS
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.WORD, reg1 = pointerReg, reg2 = pointerReg), null)
return ExpressionCodeResult(result, IRDataType.WORD, pointerReg, -1)
}
idxderef.type.isFloat -> {
val resultReg = codeGen.registers.next(IRDataType.FLOAT)
addInstr(result, IRInstruction(Opcode.LOADI, IRDataType.FLOAT, fpReg1 = resultReg, reg1 = pointerReg), null)
return ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultReg)
}
else -> throw AssemblyError("unsupported dereference type ${idxderef.type} at ${idxderef.position}")
}
}
private fun translate(deref: PtPointerDeref): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
var actualDeref = deref

View File

@@ -332,15 +332,19 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
val constIndex = arrayIndexedExpression.indexer.constIndex()
if (constIndex != null) {
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl()
if(arrayVar!=null) {
val array =arrayVar.value as? ArrayLiteral
if(array!=null) {
val value = array.value[constIndex].constValue(program)
if(value!=null) {
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, value, parent))
if(arrayIndexedExpression.plainarrayvar!=null) {
val arrayVar = arrayIndexedExpression.plainarrayvar!!.targetVarDecl()
if(arrayVar!=null) {
val array =arrayVar.value as? ArrayLiteral
if(array!=null) {
val value = array.value[constIndex].constValue(program)
if(value!=null) {
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, value, parent))
}
}
}
} else if(arrayIndexedExpression.pointerderef!=null) {
TODO("constant fold pointer[i]")
}
}
}

View File

@@ -424,27 +424,26 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
}
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
if(parent is PtrIndexedDereference)
return noModifications
if(arrayIndexedExpression.indexer.constIndex()==0) {
val dt = arrayIndexedExpression.arrayvar.inferType(program).getOrUndef()
if(dt.isPointer) {
// pointer[0] --> pointer^^
if(dt.sub==null) {
val parentExpr = parent as? BinaryExpression
if(parentExpr?.operator=="." && parentExpr.right is IdentifierReference) {
// we're part of an expression: pointer[x].ptr.ptr.field
val chain = (parentExpr.right as? IdentifierReference)?.nameInSource?.toMutableList() ?: mutableListOf()
val field = chain.removeLastOrNull()
val deref = PtrDereference(arrayIndexedExpression.arrayvar, chain, field, field==null, arrayIndexedExpression.position)
return listOf(IAstModification.ReplaceNode(parent, deref, parent.parent))
} else
throw FatalAstException("cannot dereference a 'bare' pointer to a struct, only to a basic type at ${arrayIndexedExpression.position}")
} else {
// points to a simple type, can simply dereference the pointer itself directly
val deref = PtrDereference(arrayIndexedExpression.arrayvar, emptyList(), null, true,arrayIndexedExpression.position)
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, deref, parent))
if(arrayIndexedExpression.plainarrayvar!=null) {
val dt = arrayIndexedExpression.plainarrayvar!!.inferType(program).getOrUndef()
if(dt.isPointer) {
// pointer[0] --> pointer^^
val deref = PtrDereference(arrayIndexedExpression.plainarrayvar!!, emptyList(), null, true, arrayIndexedExpression.plainarrayvar!!.position)
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression,deref, parent))
}
}
val ptrDeref = arrayIndexedExpression.pointerderef
if(ptrDeref!=null) {
val dt = ptrDeref.inferType(program).getOrUndef()
if(dt.isPointer) {
if(ptrDeref.field!=null) {
// pointer[0] --> pointer^^
val deref = PtrDereference(ptrDeref.identifier, ptrDeref.chain + ptrDeref.field!!, null, true, ptrDeref.position)
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, deref, parent))
} else {
TODO("ptr[0] rewrite")
}
}
}
}

View File

@@ -88,9 +88,6 @@ internal class AstChecker(private val program: Program,
val ppExpr = identifier.parent.parent as? BinaryExpression
if(ppExpr?.operator==".")
return // identifiers will be checked over at the BinaryExpression itself
val ppIdxExpr = identifier.parent.parent as? PtrIndexedDereference
if(ppIdxExpr!=null)
return // identifiers will be checked over at the PtrIndexedDereference itself
}
errors.undefined(identifier.nameInSource, identifier.position)
}
@@ -770,10 +767,13 @@ internal class AstChecker(private val program: Program,
fun checkRomTarget(target: AssignTarget) {
val idx=target.arrayindexed
if(idx!=null) {
val decl = idx.arrayvar.targetVarDecl()!!
if(decl.type!=VarDeclType.MEMORY && decl.zeropage!=ZeropageWish.REQUIRE_ZEROPAGE) {
// memory mapped arrays are assumed to be in RAM. If they're not.... well, POOF
errors.err("cannot assign to an array or string that is located in ROM (option romable is enabled)", assignTarget.position)
// cannot check pointer deref for rom target, assume no there.
if(idx.plainarrayvar!=null) {
val decl = idx.plainarrayvar!!.targetVarDecl()!!
if(decl.type!=VarDeclType.MEMORY && decl.zeropage!=ZeropageWish.REQUIRE_ZEROPAGE) {
// memory mapped arrays are assumed to be in RAM. If they're not.... well, POOF
errors.err("cannot assign to an array or string that is located in ROM (option romable is enabled)", assignTarget.position)
}
}
}
}
@@ -1336,7 +1336,8 @@ internal class AstChecker(private val program: Program,
leftIdentfier.targetVarDecl()?.datatype?.subType as? StructDecl
} else if(leftIndexer!=null) {
// ARRAY[x].NAME --> maybe it's a pointer dereference
leftIndexer.arrayvar.targetVarDecl()?.datatype?.subType as? StructDecl
TODO("array[x].name pointer check?")
// leftIndexer.arrayvar.targetVarDecl()?.datatype?.subType as? StructDecl
}
else null
if (struct != null) {
@@ -1366,21 +1367,22 @@ internal class AstChecker(private val program: Program,
} else if(rightIndexer!=null) {
val leftDt = expr.left.inferType(program)
if(leftDt.isStructInstance) {
// pointer[x].field[y] --> type is the dt of 'field'
var struct = leftDt.getOrUndef().subType as? StructDecl
if (struct==null) {
errors.err("cannot find struct type", expr.position)
} else {
var fieldDt = struct.getFieldType(rightIndexer.arrayvar.nameInSource.single())
if (fieldDt == null)
errors.err("no such field '${rightIndexer.arrayvar.nameInSource.single()}' in struct '${(leftDt.getOrUndef().subType as? StructDecl)?.name}'", expr.position)
else {
struct = fieldDt.subType as StructDecl
fieldDt = struct.getFieldType(rightIndexer.arrayvar.nameInSource.single())
if(fieldDt==null)
errors.err("no such field '${rightIndexer.arrayvar.nameInSource.single()}' in struct '${struct.name}'", expr.position)
}
}
TODO("pointer[x].field[y] ??")
// // pointer[x].field[y] --> type is the dt of 'field'
// var struct = leftDt.getOrUndef().subType as? StructDecl
// if (struct==null) {
// errors.err("cannot find struct type", expr.position)
// } else {
// var fieldDt = struct.getFieldType(rightIndexer.arrayvar.nameInSource.single())
// if (fieldDt == null)
// errors.err("no such field '${rightIndexer.arrayvar.nameInSource.single()}' in struct '${(leftDt.getOrUndef().subType as? StructDecl)?.name}'", expr.position)
// else {
// struct = fieldDt.subType as StructDecl
// fieldDt = struct.getFieldType(rightIndexer.arrayvar.nameInSource.single())
// if(fieldDt==null)
// errors.err("no such field '${rightIndexer.arrayvar.nameInSource.single()}' in struct '${struct.name}'", expr.position)
// }
// }
} else {
errors.err("at the moment it is not possible to chain array syntax on pointers like ...p1[x].p2[y]... use separate expressions for the time being", expr.right.position) // TODO add support for chained array syntax on pointers (rewrite ast?)
// TODO I don't think we can evaluate this because it could end up in as a struct instance, which we don't support yet... rewrite or just give an error?
@@ -1843,7 +1845,7 @@ internal class AstChecker(private val program: Program,
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
checkLongType(arrayIndexedExpression)
val target = arrayIndexedExpression.arrayvar.targetStatement(program.builtinFunctions)
val target = arrayIndexedExpression.plainarrayvar?.targetStatement(program.builtinFunctions)
if(target is VarDecl) {
if (!target.datatype.isIterable && !target.datatype.isUnsignedWord && !target.datatype.isPointer)
errors.err(
@@ -1875,26 +1877,36 @@ internal class AstChecker(private val program: Program,
} else if (index != null && index < 0) {
errors.err("index out of bounds", arrayIndexedExpression.indexer.position)
}
} else if(target is StructFieldRef) {
if(!target.type.isPointer && !target.type.isUnsignedWord)
} else if(target!=null) {
throw FatalAstException("target is not a variable")
}
if(arrayIndexedExpression.pointerderef!=null) {
val dt = arrayIndexedExpression.pointerderef!!.inferType(program)
if(!dt.isPointer && !dt.isUnsignedWord && !dt.isIterable) {
errors.err("cannot array index on this field type", arrayIndexedExpression.indexer.position)
} else {
val parentExpr = arrayIndexedExpression.parent
if(parentExpr is BinaryExpression) {
if (parentExpr.operator != ".")
errors.err("indexing requires a variable to act upon", arrayIndexedExpression.position)
} else if(parentExpr is PtrIndexedDereference) {
// all is fine
} else {
errors.err("indexing requires a variable to act upon", arrayIndexedExpression.position)
}
// else if(target is StructFieldRef) {
// if(!target.type.isPointer && !target.type.isUnsignedWord)
// errors.err("cannot array index on this field type", arrayIndexedExpression.indexer.position)
// } else {
// val parentExpr = arrayIndexedExpression.parent
// if(parentExpr is BinaryExpression) {
// if (parentExpr.operator != ".")
// errors.err("indexing requires a variable to act upon", arrayIndexedExpression.position)
// } else if(parentExpr is PtrIndexedDereference) {
// // all is fine
// } else {
// errors.err("indexing requires a variable to act upon", arrayIndexedExpression.position)
// }
// }
}
// check index value 0..255 if the index variable is not a pointer
val dtxNum = arrayIndexedExpression.indexer.indexExpr.inferType(program)
if(dtxNum.isKnown) {
val arrayVarDt = arrayIndexedExpression.arrayvar.inferType(program)
if (!arrayVarDt.isPointer && !(dtxNum issimpletype BaseDataType.UBYTE) && !(dtxNum issimpletype BaseDataType.BYTE))
val arrayVarDt = arrayIndexedExpression.plainarrayvar?.inferType(program)
if (arrayVarDt!=null && !arrayVarDt.isPointer && !(dtxNum issimpletype BaseDataType.UBYTE) && !(dtxNum issimpletype BaseDataType.BYTE))
errors.err("array indexing is limited to byte size 0..255", arrayIndexedExpression.position)
}
@@ -1999,12 +2011,12 @@ internal class AstChecker(private val program: Program,
private fun allowedMemoryAccessAddressExpression(addressExpression: Expression, program: Program): Boolean {
val dt = addressExpression.inferType(program)
if(dt.isUnsignedWord || (dt.isPointer && dt.getOrUndef().sub==BaseDataType.UBYTE))
if(dt.isUnsignedWord || (dt.isPointer && dt.getOrUndef().sub?.isByte==true))
return true
val tc = addressExpression as? TypecastExpression
if(tc!=null && tc.implicit) {
val dt = tc.expression.inferType(program)
if(dt.isUnsignedWord || (dt.isPointer && dt.getOrUndef().sub==BaseDataType.UBYTE))
if(dt.isUnsignedWord || (dt.isPointer && dt.getOrUndef().sub?.isByte==true))
return true
}
return false
@@ -2372,12 +2384,6 @@ internal class AstChecker(private val program: Program,
errors.err("on..goto index must be an unsigned byte", onGoto.index.position)
}
}
override fun visit(idxderef: PtrIndexedDereference) {
val dt = idxderef.indexed.arrayvar.inferType(program)
if(!dt.isUnsignedWord && !dt.isPointer)
errors.err("cannot array index on this field type", idxderef.indexed.position)
}
}
internal fun checkUnusedReturnValues(call: FunctionCallStatement, target: Statement, errors: IErrorReporter) {

View File

@@ -216,12 +216,17 @@ _after:
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
// replace pointervar[word] by @(pointervar+word) to avoid the
// "array indexing is limited to byte size 0..255" error for pointervariables.
if(arrayIndexedExpression.pointerderef!=null) {
return noModifications
}
val indexExpr = arrayIndexedExpression.indexer.indexExpr
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl()
val arrayVar = arrayIndexedExpression.plainarrayvar!!.targetVarDecl()
if(arrayVar!=null && (arrayVar.datatype.isUnsignedWord || (arrayVar.datatype.isPointer && arrayVar.datatype.sub==BaseDataType.UBYTE))) {
val wordIndex = TypecastExpression(indexExpr, DataType.UWORD, true, indexExpr.position)
val address = BinaryExpression(
arrayIndexedExpression.arrayvar.copy(),
arrayIndexedExpression.plainarrayvar!!.copy(),
"+",
wordIndex,
arrayIndexedExpression.position
@@ -243,23 +248,24 @@ _after:
val memread = DirectMemoryRead(address, arrayIndexedExpression.position)
listOf(IAstModification.ReplaceNode(arrayIndexedExpression, memread, parent))
}
} else if(arrayVar!=null && (arrayVar.datatype.isPointer || arrayVar.datatype.isArray)) {
} else if(arrayVar!=null && (arrayVar.type==VarDeclType.MEMORY || arrayVar.datatype.isString || arrayVar.datatype.isPointer || arrayVar.datatype.isArray)) {
return noModifications
} else {
} else if(arrayVar!=null) {
// it could be a pointer dereference instead of a simple array variable
val dt = arrayIndexedExpression.arrayvar.traverseDerefChainForDt(null)
if(dt.isUnsignedWord) {
// ptr.field[index] --> @(ptr.field + index)
val index = arrayIndexedExpression.indexer.indexExpr
val address = BinaryExpression(arrayIndexedExpression.arrayvar.copy(), "+", index, arrayIndexedExpression.position)
if(parent is AssignTarget) {
val memwrite = DirectMemoryWrite(address, arrayIndexedExpression.position)
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, memwrite, parent))
} else {
val memread = DirectMemoryRead(address, arrayIndexedExpression.position)
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, memread, parent))
}
}
TODO("deref[word] rewrite ???? $arrayIndexedExpression")
// val dt = arrayIndexedExpression.plainarrayvar!!.traverseDerefChainForDt(null)
// if(dt.isUnsignedWord) {
// // ptr.field[index] --> @(ptr.field + index)
// val index = arrayIndexedExpression.indexer.indexExpr
// val address = BinaryExpression(arrayIndexedExpression.arrayvar.copy(), "+", index, arrayIndexedExpression.position)
// if(parent is AssignTarget) {
// val memwrite = DirectMemoryWrite(address, arrayIndexedExpression.position)
// return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, memwrite, parent))
// } else {
// val memread = DirectMemoryRead(address, arrayIndexedExpression.position)
// return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, memread, parent))
// }
// }
}
return noModifications
}
@@ -396,9 +402,6 @@ _after:
override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
if(parent is PtrIndexedDereference || parent.parent is PtrIndexedDereference)
return noModifications
if(identifier.nameInSource.size>1 && (identifier.firstTarget() as? VarDecl)?.datatype?.isPointer==true) {
// the a.b.c.d is be a pointer dereference chain ending in a struct field; a^^.b^^.c^^.d
@@ -426,13 +429,7 @@ _after:
struct = fieldDt.subType as StructDecl
}
if(parent is ArrayIndexedExpression) {
if(struct.getFieldType(field!!)!!.isUnsignedWord) {
return noModifications
}
val deref = PtrIndexedDereference(parent, parent.position)
return listOf(IAstModification.ReplaceNode(parent, deref, parent.parent))
} else if (parent !is PtrDereference) {
if (parent !is PtrDereference) {
val deref = PtrDereference(IdentifierReference(identifier.nameInSource.take(i), identifier.position), chain, field, false, identifier.position)
return listOf(IAstModification.ReplaceNode(identifier, deref, parent))
}
@@ -472,7 +469,12 @@ _after:
assignIndex = Assignment(varTarget, ongoto.index, AssignmentOrigin.USERCODE, ongoto.position)
}
val callTarget = ArrayIndexedExpression(IdentifierReference(listOf(jumplistArray.name), jumplistArray.position), ArrayIndex(indexValue.copy(), indexValue.position), ongoto.position)
val callTarget = ArrayIndexedExpression(
IdentifierReference(listOf(jumplistArray.name), jumplistArray.position),
null,
ArrayIndex(indexValue.copy(), indexValue.position),
ongoto.position
)
val callIndexed = AnonymousScope(mutableListOf(), ongoto.position)
if(ongoto.isCall) {
callIndexed.statements.add(FunctionCallStatement(IdentifierReference(listOf("call"), ongoto.position), mutableListOf(callTarget), true, ongoto.position))

View File

@@ -1,9 +1,6 @@
package prog8.compiler.astprocessing
import prog8.ast.IFunctionCall
import prog8.ast.IStatementContainer
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.*
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
@@ -169,10 +166,14 @@ internal class LiteralsToAutoVarsAndRecombineIdentifiers(private val program: Pr
// maybe recombine IDENTIFIER . ARRAY[IDX] --> COMBINEDIDENTIFIER[IDX]
val leftTarget = leftIdent.targetStatement()
if(leftTarget==null || leftTarget !is StructDecl) {
val combinedName = leftIdent.nameInSource + rightIndex.arrayvar.nameInSource
val combined = IdentifierReference(combinedName, leftIdent.position)
val indexer = ArrayIndexedExpression(combined, rightIndex.indexer, leftIdent.position)
return listOf(IAstModification.ReplaceNode(expr, indexer, parent))
if(rightIndex.plainarrayvar!=null) {
val combinedName = leftIdent.nameInSource + rightIndex.plainarrayvar!!.nameInSource
val combined = IdentifierReference(combinedName, leftIdent.position)
val indexer = ArrayIndexedExpression(combined, null, rightIndex.indexer, leftIdent.position)
return listOf(IAstModification.ReplaceNode(expr, indexer, parent))
} else {
throw FatalAstException("didn't expect pointer[idx] in this phase already")
}
}
}
}

View File

@@ -100,22 +100,9 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
is TypecastExpression -> transform(expr)
is IfExpression -> transform(expr)
is PtrDereference -> transform(expr)
is PtrIndexedDereference -> transform(expr)
}
}
private fun transform(idxderef: PtrIndexedDereference): PtPointerIndexedDeref {
val type = idxderef.inferType(program).getOrElse {
throw FatalAstException("unknown dt")
}
val derefType = if(type.isPointer) DataType.forDt(type.sub!!) else type
val deref = PtPointerIndexedDeref(derefType, idxderef.position)
val identifier = PtIdentifier(idxderef.indexed.arrayvar.nameInSource.joinToString("."), DataType.pointer(derefType), idxderef.indexed.arrayvar.position)
deref.add(identifier)
deref.add(transformExpression(idxderef.indexed.indexer.indexExpr))
return deref
}
private fun transform(deref: PtrDereference): PtPointerDeref {
val type = deref.inferType(program).getOrElse {
throw FatalAstException("unknown dt")
@@ -244,7 +231,6 @@ 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.pointerIndexedDeref!=null -> target.add(transform(srcTarget.pointerIndexedDeref!!))
!srcTarget.void -> throw FatalAstException("invalid AssignTarget")
}
return target
@@ -699,14 +685,20 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
}
private fun transform(srcArr: ArrayIndexedExpression): PtArrayIndexer {
val dt = srcArr.arrayvar.inferType(program)
if(!dt.isArray && !dt.isString && !dt.isPointer)
throw FatalAstException("array indexing can only be used on array, string or pointer variables ${srcArr.position}")
val eltType = srcArr.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
val array = PtArrayIndexer(eltType, srcArr.position)
array.add(transform(srcArr.arrayvar))
array.add(transformExpression(srcArr.indexer.indexExpr))
return array
if(srcArr.plainarrayvar!=null) {
val dt = srcArr.plainarrayvar!!.inferType(program)
if (!dt.isArray && !dt.isString && !dt.isPointer)
throw FatalAstException("array indexing can only be used on array, string or pointer variables ${srcArr.position}")
val eltType = srcArr.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
val array = PtArrayIndexer(eltType, srcArr.position)
array.add(transform(srcArr.plainarrayvar!!))
array.add(transformExpression(srcArr.indexer.indexExpr))
return array
}
if(srcArr.pointerderef!=null) {
TODO("transform pointer index")
}
throw FatalAstException("expected plain array variable or pointer dereference")
}
private fun transform(srcArr: ArrayLiteral): PtArray {

View File

@@ -28,7 +28,6 @@ private fun checkForPointerTypesOn6502(program: PtProgram, errors: IErrorReporte
//is PtBinaryExpression -> if(node.left.type.isPointer || node.right.type.isPointer) errors.err("cannot do pointer arithmetic yet on 6502 target $node", node.position)
is PtIdentifier -> if(node.type.isPointer) errors.err("cannot use pointer type yet on 6502 target $node", node.position)
is PtPointerDeref -> errors.err("cannot use pointer type yet on 6502 target $node", node.position)
is PtPointerIndexedDeref -> errors.err("cannot use pointer type yet on 6502 target $node", node.position)
is PtPrefix -> if(node.type.isPointer) errors.err("cannot use pointer type yet on 6502 target $node", node.position)
is PtTypeCast -> if(node.type.isPointer) errors.err("cannot use pointer type yet on 6502 target $node", node.position)
is PtStructDecl -> errors.err("cannot use struct type yet on 6502 target $node", node.position)
@@ -62,7 +61,6 @@ private fun processSubtypesIntoStReferences(program: PtProgram, st: SymbolTable)
when(node) {
is IPtVariable -> fixSubtype(node.type)
is PtPointerDeref -> fixSubtype(node.type)
is PtPointerIndexedDeref -> fixSubtype(node.type)
is PtStructDecl -> node.fields.forEach { fixSubtype(it.first) }
is PtAsmSub -> node.returns.forEach { fixSubtype(it.second) }
is PtExpression -> fixSubtype(node.type)

View File

@@ -636,7 +636,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
val idxDt = arrayIndexedExpression.indexer.indexExpr.inferType(program).getOrUndef()
if(idxDt.base.largerSizeThan(smaller.type)) {
val newIdx = ArrayIndex(smaller, smaller.position)
val newIndexer = ArrayIndexedExpression(arrayIndexedExpression.arrayvar, newIdx, arrayIndexedExpression.position)
val newIndexer = ArrayIndexedExpression(arrayIndexedExpression.plainarrayvar, arrayIndexedExpression.pointerderef, newIdx, arrayIndexedExpression.position)
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, newIndexer, parent))
}
}

View File

@@ -428,17 +428,21 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
val index = arrayIndexedExpression.indexer.constIndex()
if(index!=null && index<0) {
val target = arrayIndexedExpression.arrayvar.targetVarDecl()
val arraysize = target?.arraysize?.constIndex()
if(arraysize!=null) {
if(arraysize+index < 0) {
errors.err("index out of bounds", arrayIndexedExpression.position)
return noModifications
if(arrayIndexedExpression.plainarrayvar!=null) {
val target = arrayIndexedExpression.plainarrayvar!!.targetVarDecl()
val arraysize = target?.arraysize?.constIndex()
if(arraysize!=null) {
if(arraysize+index < 0) {
errors.err("index out of bounds", arrayIndexedExpression.position)
return noModifications
}
// 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)
}
// 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)
} else if(arrayIndexedExpression.pointerderef!=null) {
TODO("cleanup pointer indexing")
}
}
return noModifications

View File

@@ -322,7 +322,7 @@ class TestMemory: FunSpec({
test("array not in mapped IO ram") {
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.arrayFor(BaseDataType.UBYTE), ZeropageWish.DONTCARE,
SplitWish.DONTCARE, null, "address", emptyList(), null, false, 0u, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), null, ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, null, false, position = Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
@@ -336,7 +336,7 @@ class TestMemory: FunSpec({
val address = 0x1000u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.arrayFor(BaseDataType.UBYTE), ZeropageWish.DONTCARE,
SplitWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, 0u, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), null, ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, null, false, position = Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)
@@ -350,7 +350,7 @@ class TestMemory: FunSpec({
val address = 0xd800u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.arrayFor(BaseDataType.UBYTE), ZeropageWish.DONTCARE,
SplitWish.DONTCARE, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, 0u, false, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), null, ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, null, false, position = Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, mutableListOf(decl, assignment), Position.DUMMY)

View File

@@ -7,6 +7,8 @@ import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.string.shouldContain
import io.kotest.matchers.types.instanceOf
import prog8.ast.expressions.ArrayIndexedExpression
import prog8.ast.expressions.DirectMemoryRead
import prog8.ast.expressions.PtrDereference
import prog8.ast.statements.Assignment
import prog8.ast.statements.VarDecl
@@ -193,6 +195,8 @@ main {
cx16.r1 = matchstate^^.next^^.next^^.ptr
cx16.r2 = matchstate.ptr
cx16.r3 = matchstate.next.next.ptr
cx16.r4 = matchstate.ptr^^
cx16.r5 = matchstate.next.next.ptr^^
matchstate^^.ptr = 2222
matchstate^^.next^^.next^^.ptr = 2222
@@ -203,7 +207,7 @@ main {
val result = compileText(VMTarget(), false, src, outputDir, writeAssembly = false)!!
val st = result.compilerAst.entrypoint.statements
st.size shouldBe 11
st.size shouldBe 13
val a0v = (st[2] as Assignment).value as PtrDereference
a0v.identifier.nameInSource shouldBe listOf("matchstate")
a0v.chain.size shouldBe 0
@@ -228,31 +232,98 @@ main {
a3v.field shouldBe "ptr"
a3v.derefPointerValue shouldBe false
val t0 = (st[6] as Assignment).target.pointerDereference!!
val a4v = (st[6] as Assignment).value as PtrDereference
a4v.identifier.nameInSource shouldBe listOf("matchstate")
a4v.chain shouldBe listOf("ptr")
a4v.field shouldBe null
a4v.derefPointerValue shouldBe true
val a5v = (st[7] as Assignment).value as PtrDereference
a5v.identifier.nameInSource shouldBe listOf("matchstate")
a5v.chain shouldBe listOf("next", "next", "ptr")
a5v.field shouldBe null
a5v.derefPointerValue shouldBe true
val t0 = (st[8] as Assignment).target.pointerDereference!!
t0.derefPointerValue shouldBe false
t0.identifier.nameInSource shouldBe listOf("matchstate")
t0.chain.size shouldBe 0
t0.field shouldBe "ptr"
val t1 = (st[7] as Assignment).target.pointerDereference!!
val t1 = (st[9] as Assignment).target.pointerDereference!!
t1.derefPointerValue shouldBe false
t1.identifier.nameInSource shouldBe listOf("matchstate")
t1.chain shouldBe listOf("next", "next")
t1.field shouldBe "ptr"
val t2 = (st[8] as Assignment).target.pointerDereference!!
val t2 = (st[10] as Assignment).target.pointerDereference!!
t2.derefPointerValue shouldBe false
t2.identifier.nameInSource shouldBe listOf("matchstate")
t2.chain.size shouldBe 0
t2.field shouldBe "ptr"
val t3 = (st[9] as Assignment).target.pointerDereference!!
val t3 = (st[11] as Assignment).target.pointerDereference!!
t3.derefPointerValue shouldBe false
t3.identifier.nameInSource shouldBe listOf("matchstate")
t3.chain shouldBe listOf("next", "next")
t3.field shouldBe "ptr"
}
test("word size pointer indexing on pointers") {
val src="""
%option enable_floats
main {
struct List {
^^uword s
ubyte n
^^List next
}
sub start() {
ubyte[10] array
uword @shared wordptr
^^bool @shared boolptr
^^float @shared floatptr
^^byte @shared byteptr
^^ubyte @shared ubyteptr
^^List @shared listptr
^^List @shared listptr2
bool @shared zz
float @shared fl
byte @shared bb
zz = boolptr[999]
fl = floatptr[999]
bb = byteptr[999]
cx16.r0L = ubyteptr[999]
cx16.r1L = wordptr[999]
cx16.r2L = array[9]
listptr2 = listptr[999]
}
}"""
val result = compileText(VMTarget(), false, src, outputDir, writeAssembly = false)!!
val st = result.compilerAst.entrypoint.statements
st.size shouldBe 28
val a_zz = (st[20] as Assignment).value
a_zz shouldBe instanceOf<ArrayIndexedExpression>()
val a_fl = (st[21] as Assignment).value
a_fl shouldBe instanceOf<ArrayIndexedExpression>()
val a_bb = (st[22] as Assignment).value
a_bb shouldBe instanceOf<ArrayIndexedExpression>()
val a_r0 = (st[23] as Assignment).value
a_r0 shouldBe instanceOf<DirectMemoryRead>()
val a_r1 = (st[24] as Assignment).value
a_r1 shouldBe instanceOf<DirectMemoryRead>()
val a_r2 = (st[25] as Assignment).value
a_r2 shouldBe instanceOf<ArrayIndexedExpression>()
val a_lptr2 = (st[25] as Assignment).value
a_lptr2 shouldBe instanceOf<ArrayIndexedExpression>()
}
test("block scoping still parsed correctly") {
val src="""
main {
@@ -551,6 +622,9 @@ main {
}
sub start() {
^^List @shared l1 = List()
bool ss = l1.s[1]
ubyte ub = l1.n[1]
uword uw = l1.ptr[1]
l1.s[1] = 4444
l1.n[1] = true
l1.ptr[1] = 4444
@@ -559,10 +633,15 @@ main {
val errors = ErrorReporterForTests()
compileText(VMTarget(), false, src, outputDir, errors=errors) shouldBe null
errors.errors.size shouldBe 4
errors.errors[0] shouldContain "cannot array index"
errors.errors.size shouldBe 8
errors.errors[0] shouldContain "invalid assignment value"
errors.errors[1] shouldContain "cannot array index"
errors.errors[2] shouldContain "out of range"
errors.errors[2] shouldContain "invalid assignment value"
errors.errors[3] shouldContain "cannot array index"
errors.errors[4] shouldContain "cannot array index"
errors.errors[5] shouldContain "cannot array index"
errors.errors[6] shouldContain "out of range"
errors.errors[7] shouldContain "cannot assign word to byte"
}
test("dereferences of ptr variables mark those as used in the callgraph") {
@@ -604,24 +683,26 @@ main {
}
sub start() {
^^List l1 = List()
cx16.r0 = l1.s[0]
l1.s[0] = 4242
cx16.r1 = l1.s^^
^^word @shared wptr
cx16.r1 = l1.s^^
cx16.r0 = l1.s[0]
cx16.r2 = l1^^.s^^
l1.s[0] = 4242
cx16.r1 = l1.s^^
cx16.r0s = wptr[0]
cx16.r1s = wptr^^
wptr[0] = 4242
cx16.r1s = wptr^^
wptr[0] = 4242
}
}"""
val result = compileText(VMTarget(), true, src, outputDir, writeAssembly = false)!!
val st = result.compilerAst.entrypoint.statements
st.size shouldBe 11
val dr0 = (st[2] as Assignment).value as PtrDereference
val dr1 = (st[3] as Assignment).target.pointerDereference!!
val dr2 = (st[4] as Assignment).value as PtrDereference
val dr0 = (st[4] as Assignment).value as PtrDereference
val dr1 = (st[5] as Assignment).target.pointerDereference!!
val dr2 = (st[6] as Assignment).value as PtrDereference
val dr3 = (st[7] as Assignment).value as PtrDereference
val dr4 = (st[8] as Assignment).value as PtrDereference

View File

@@ -124,16 +124,17 @@ class TestAstChecks: FunSpec({
main {
sub start() {
&ubyte a = 10000
uword @shared z = 500
a[4] = (z % 3) as ubyte
cx16.r0L = a[4]
a[4] = cx16.r1L
}
}
"""
val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
compileText(C64Target(), true, text, outputDir, writeAssembly = true, errors=errors)
errors.errors.size shouldBe 1
errors.errors.size shouldBe 2
errors.warnings.size shouldBe 0
errors.errors[0] shouldContain "indexing requires"
errors.errors[1] shouldContain "indexing requires"
}
test("unicode in identifier names is working") {

View File

@@ -420,7 +420,8 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
}
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
arrayIndexedExpression.arrayvar.accept(this)
arrayIndexedExpression.plainarrayvar?.accept(this)
arrayIndexedExpression.pointerderef?.accept(this)
output("[")
arrayIndexedExpression.indexer.indexExpr.accept(this)
output("]")
@@ -434,7 +435,6 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
assignTarget.identifier?.accept(this)
assignTarget.arrayindexed?.accept(this)
assignTarget.pointerDereference?.accept(this)
assignTarget.pointerIndexedDeref?.accept(this)
val multi = assignTarget.multi
if (multi != null) {
multi.dropLast(1).forEach { target ->
@@ -558,11 +558,6 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
output(".${deref.field}")
}
override fun visit(idxderef: PtrIndexedDereference) {
idxderef.indexed.accept(this)
output("^^")
}
override fun visit(field: StructFieldRef) {
throw FatalAstException("struct field ref shouldn't occur as part of the AST tree ")
}

View File

@@ -402,9 +402,9 @@ private fun Assign_targetContext.toAst() : AssignTarget {
)
is ArrayindexedTargetContext -> {
val ax = arrayindexed()
val arrayvar = ax.scoped_identifier().toAst()
val plainarrayvar = ax.scoped_identifier().toAst()
val index = ax.arrayindex().toAst()
val arrayindexed = ArrayIndexedExpression(arrayvar, index, ax.toPosition())
val arrayindexed = ArrayIndexedExpression(plainarrayvar, null, index, ax.toPosition())
AssignTarget(null, arrayindexed, null, null, false, position = toPosition())
}
is VoidTargetContext -> {
@@ -412,11 +412,7 @@ private fun Assign_targetContext.toAst() : AssignTarget {
}
is PointerDereferenceTargetContext -> {
val deref = this.pointerdereference().toAst()
AssignTarget(null, null, null, null, false, deref, null, deref.position)
}
is PointerIndexedDerefTargetContext -> {
val deref = this.pointerindexedderef().toAst()
AssignTarget(null, null, null, null, false, null, deref, deref.position)
AssignTarget(null, null, null, null, false, deref, deref.position)
}
else -> throw FatalAstException("weird assign target node $this")
}
@@ -602,9 +598,9 @@ private fun ExpressionContext.toAst(insideParentheses: Boolean=false) : Expressi
if(arrayindexed()!=null) {
val ax = arrayindexed()
val identifier = ax.scoped_identifier().toAst()
val plainarrayvar = ax.scoped_identifier().toAst()
val index = ax.arrayindex().toAst()
return ArrayIndexedExpression(identifier, index, ax.toPosition())
return ArrayIndexedExpression(plainarrayvar, null, index, ax.toPosition())
}
if(scoped_identifier()!=null)
@@ -666,21 +662,10 @@ private fun ExpressionContext.toAst(insideParentheses: Boolean=false) : Expressi
val deref = pointerdereference()?.toAst()
if(deref!=null) return deref
val indexedderef = pointerindexedderef()?.toAst()
if(indexedderef!=null) return indexedderef
throw FatalAstException(text)
}
private fun PointerindexedderefContext.toAst(): PtrIndexedDereference {
val ax = arrayindexed()
val arrayvar = ax.scoped_identifier().toAst()
val index = ax.arrayindex().toAst()
val arrayindexed = ArrayIndexedExpression(arrayvar, index, ax.toPosition())
return PtrIndexedDereference(arrayindexed, toPosition())
}
private fun PointerdereferenceContext.toAst(): PtrDereference {
val scopeprefix = prefix?.toAst()
val derefchain = derefchain()!!.singlederef()!!.map { it.identifier().text }

View File

@@ -40,10 +40,7 @@ sealed class Expression: Node {
else
other.left isSameAs left && other.right isSameAs right
}
is ArrayIndexedExpression -> {
(other is ArrayIndexedExpression && other.arrayvar.nameInSource == arrayvar.nameInSource
&& other.indexer isSameAs indexer)
}
is ArrayIndexedExpression -> isSameArrayIndexedAs(other)
is DirectMemoryRead -> {
(other is DirectMemoryRead && other.addressExpression isSameAs addressExpression)
}
@@ -242,9 +239,12 @@ class BinaryExpression(
if (leftIdentfier != null) {
// PTR . FIELD
leftIdentfier.targetVarDecl()?.datatype?.subType as? StructDecl
} else if(leftIndexer!=null) {
} else if(leftIndexer!=null && rightIdentifier.nameInSource.size==1) {
// ARRAY[x].NAME --> maybe it's a pointer dereference
leftIndexer.arrayvar.targetVarDecl()?.datatype?.subType as? StructDecl
val dt = leftIndexer.inferType(program).getOrUndef()
if(dt.isPointer) {
dt.dereference().subType as? StructDecl
} else null
} else if(leftExpr!=null) {
// SOMEEXPRESSION . NAME
val leftDt = leftExpr.inferType(program)
@@ -269,17 +269,18 @@ class BinaryExpression(
} else if(rightIndexer!=null) {
if(leftDt.isStructInstance) {
// pointer[x].field[y] --> type is the dt of 'field'
var fieldDt = (leftDt.getOrUndef().subType as? StructDecl)?.getFieldType(rightIndexer.arrayvar.nameInSource.single())
if (fieldDt == null)
InferredTypes.unknown()
else {
val struct = fieldDt.subType as StructDecl
fieldDt = struct.getFieldType(rightIndexer.arrayvar.nameInSource.single())
if(fieldDt!=null)
if(fieldDt.isUndefined) InferredTypes.unknown() else InferredTypes.knownFor(fieldDt)
else
InferredTypes.unknown()
}
TODO("pointer[x].field[y] ?????")
// var fieldDt = (leftDt.getOrUndef().subType as? StructDecl)?.getFieldType(rightIndexer.arrayvar.nameInSource.single())
// if (fieldDt == null)
// InferredTypes.unknown()
// else {
// val struct = fieldDt.subType as StructDecl
// fieldDt = struct.getFieldType(rightIndexer.arrayvar.nameInSource.single())
// if(fieldDt!=null)
// if(fieldDt.isUndefined) InferredTypes.unknown() else InferredTypes.knownFor(fieldDt)
// else
// InferredTypes.unknown()
// }
} else
InferredTypes.unknown() // TODO("something.field[x] at ${right.position}")
// TODO I don't think we can evaluate this type because it could end up in as a struct instance, which we don't support yet... rewrite or just give an error?
@@ -386,21 +387,30 @@ class BinaryExpression(
}
}
class ArrayIndexedExpression(var arrayvar: IdentifierReference,
class ArrayIndexedExpression(var plainarrayvar: IdentifierReference?,
var pointerderef: PtrDereference?,
val indexer: ArrayIndex,
override val position: Position) : Expression() {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
this.parent = parent
arrayvar.linkParents(this)
plainarrayvar?.linkParents(this)
pointerderef?.linkParents(this)
indexer.linkParents(this)
}
override val isSimple = indexer.indexExpr is NumericLiteral || indexer.indexExpr is IdentifierReference
override fun replaceChildNode(node: Node, replacement: Node) {
when {
node===arrayvar -> arrayvar = replacement as IdentifierReference
when (replacement) {
is IdentifierReference -> {
plainarrayvar = replacement
pointerderef = null
}
is PtrDereference -> {
plainarrayvar = null
pointerderef = replacement
}
else -> throw FatalAstException("invalid replace")
}
replacement.parent = this
@@ -410,39 +420,58 @@ class ArrayIndexedExpression(var arrayvar: IdentifierReference,
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
override fun referencesIdentifier(nameInSource: List<String>) = arrayvar.referencesIdentifier(nameInSource) || indexer.referencesIdentifier(nameInSource)
override fun referencesIdentifier(nameInSource: List<String>) =
plainarrayvar?.referencesIdentifier(nameInSource)==true || pointerderef?.referencesIdentifier(nameInSource)==true || indexer.referencesIdentifier(nameInSource)
override fun inferType(program: Program): InferredTypes.InferredType {
val target = arrayvar.targetStatement(program.builtinFunctions)
if (target is VarDecl) {
return when {
target.datatype.isString || target.datatype.isUnsignedWord -> InferredTypes.knownFor(BaseDataType.UBYTE)
target.datatype.isArray -> InferredTypes.knownFor(target.datatype.elementType())
target.datatype.isPointer -> {
if(target.datatype.subType!=null)
InferredTypes.knownFor(DataType.structInstance(target.datatype.subType))
else if(target.datatype.subTypeFromAntlr!=null)
InferredTypes.unknown()
else
InferredTypes.knownFor(target.datatype.sub!!)
if(plainarrayvar!=null) {
val target = plainarrayvar!!.targetStatement()
if(target is VarDecl) {
return when {
target.datatype.isString || target.datatype.isUnsignedWord -> InferredTypes.knownFor(BaseDataType.UBYTE)
target.datatype.isArray -> InferredTypes.knownFor(target.datatype.elementType())
target.datatype.isPointer -> InferredTypes.knownFor(target.datatype.dereference())
else -> InferredTypes.knownFor(target.datatype)
}
else -> InferredTypes.knownFor(target.datatype)
}
} else {
val dt = arrayvar.inferType(program).getOrUndef()
if(dt.isPointer) {
if(dt.sub!=null)
return InferredTypes.knownFor(dt.sub!!)
} else if(target is StructFieldRef) {
return InferredTypes.knownFor(target.type)
} else if(target==null) {
return InferredTypes.unknown()
} else
TODO("infer type from target $target")
} else if(pointerderef!=null) {
val dt= pointerderef!!.inferType(program).getOrUndef()
return when {
dt.isString || dt.isUnsignedWord -> InferredTypes.knownFor(BaseDataType.UBYTE)
dt.isArray -> InferredTypes.knownFor(dt.elementType())
dt.isPointer -> InferredTypes.knownFor(dt.dereference())
else -> InferredTypes.unknown()
}
}
return InferredTypes.unknown()
}
override fun toString(): String {
return "ArrayIndexed(ident=$arrayvar, idx=$indexer; pos=$position)"
return if(plainarrayvar!=null)
"ArrayIndexed(arrayvar=$plainarrayvar, idx=$indexer; pos=$position)"
else if(pointerderef!=null)
"ArrayIndexed(ptr=$pointerderef, idx=$indexer; pos=$position)"
else
"??????"
}
override fun copy() = ArrayIndexedExpression(plainarrayvar?.copy(), pointerderef?.copy(), indexer.copy(), position)
fun isSameArrayIndexedAs(other: Expression): Boolean {
if(other !is ArrayIndexedExpression || other.indexer!=indexer)
return false
if(plainarrayvar?.nameInSource != other.plainarrayvar?.nameInSource)
return false
if(pointerderef!=null)
return pointerderef!!.isSamePointerDeref(other.pointerderef)
return true
}
override fun copy() = ArrayIndexedExpression(arrayvar.copy(), indexer.copy(), position)
}
class TypecastExpression(var expression: Expression, var type: DataType, val implicit: Boolean, override val position: Position) : Expression() {
@@ -1618,46 +1647,6 @@ class IfExpression(var condition: Expression, var truevalue: Expression, var fal
}
}
class PtrIndexedDereference(val indexed: ArrayIndexedExpression, override val position: Position) : Expression() {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
this.parent = parent
indexed.linkParents(this)
}
override val isSimple = false
override fun copy() = PtrIndexedDereference(indexed.copy(), position)
override fun constValue(program: Program): NumericLiteral? = null
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
override fun inferType(program: Program): InferredTypes.InferredType {
val parentExpr = parent as? BinaryExpression
if(parentExpr?.operator==".") {
TODO("cannot determine type of dereferenced indexed pointer(?) as part of a larger dereference expression")
}
val vardecl = indexed.arrayvar.targetVarDecl()
if(vardecl!=null &&vardecl.datatype.isPointer)
return InferredTypes.knownFor(vardecl.datatype.dereference())
if(parent is AssignTarget || parent is Assignment) {
val dt = indexed.arrayvar.traverseDerefChainForDt(null)
return when {
dt.isUndefined -> InferredTypes.unknown()
dt.isUnsignedWord -> InferredTypes.knownFor(BaseDataType.UBYTE)
dt.isPointer -> InferredTypes.knownFor(dt.dereference())
else -> InferredTypes.unknown()
}
}
return InferredTypes.unknown()
}
override fun replaceChildNode(node: Node, replacement: Node) =
throw FatalAstException("can't replace here")
override fun referencesIdentifier(nameInSource: List<String>) = indexed.referencesIdentifier(nameInSource)
}
class PtrDereference(
val identifier: IdentifierReference,
val chain: List<String>,
@@ -1666,6 +1655,7 @@ class PtrDereference(
override val position: Position
) : Expression() {
// TODO why both identifier and chain?
// TODO why both chain and field? field is just the last entry of chain?
override lateinit var parent: Node
@@ -1731,6 +1721,19 @@ class PtrDereference(
override fun replaceChildNode(node: Node, replacement: Node) =
throw FatalAstException("can't replace here")
override fun referencesIdentifier(nameInSource: List<String>) = identifier.referencesIdentifier(nameInSource)
fun isSamePointerDeref(other: Expression?): Boolean {
if(other==null || other !is PtrDereference)
return false
if(derefPointerValue != other.derefPointerValue)
return false
if(identifier.nameInSource != other.identifier.nameInSource)
return false
if(chain != other.chain)
return false
if(field != other.field)
return false
return true
}
}
fun invertCondition(cond: Expression, program: Program): Expression {

View File

@@ -593,7 +593,6 @@ data class AssignTarget(
val multi: List<AssignTarget>?,
val void: Boolean,
var pointerDereference: PtrDereference? = null,
var pointerIndexedDeref : PtrIndexedDereference? = null,
override val position: Position
) : Node {
override lateinit var parent: Node
@@ -604,7 +603,6 @@ data class AssignTarget(
arrayindexed?.linkParents(this)
memoryAddress?.linkParents(this)
pointerDereference?.linkParents(this)
pointerIndexedDeref?.linkParents(this)
multi?.forEach { it.linkParents(this) }
}
@@ -614,22 +612,18 @@ data class AssignTarget(
identifier = null
arrayindexed = null
pointerDereference = null
pointerIndexedDeref = null
when (replacement) {
is IdentifierReference -> identifier = replacement
is PtrDereference -> pointerDereference = replacement
is PtrIndexedDereference -> pointerIndexedDeref = replacement
else -> throw FatalAstException("invalid replacement for AssignTarget.identifier: $replacement")
}
}
node === arrayindexed -> {
identifier = null
pointerDereference = null
pointerIndexedDeref = null
arrayindexed = null
memoryAddress = null
when (replacement) {
is PtrIndexedDereference -> pointerIndexedDeref = replacement
is ArrayIndexedExpression -> arrayindexed = replacement
is DirectMemoryWrite -> memoryAddress = replacement
is PtrDereference -> pointerDereference = replacement
@@ -651,7 +645,6 @@ data class AssignTarget(
multi?.toList(),
void,
pointerDereference?.copy(),
pointerIndexedDeref?.copy(),
position
)
override fun referencesIdentifier(nameInSource: List<String>): Boolean =
@@ -659,7 +652,6 @@ data class AssignTarget(
arrayindexed?.referencesIdentifier(nameInSource)==true ||
memoryAddress?.referencesIdentifier(nameInSource)==true ||
pointerDereference?.referencesIdentifier(nameInSource)==true ||
pointerIndexedDeref?.referencesIdentifier(nameInSource)==true ||
multi?.any { it.referencesIdentifier(nameInSource)}==true
fun inferType(program: Program): InferredTypes.InferredType {
@@ -672,7 +664,6 @@ data class AssignTarget(
arrayindexed != null -> arrayindexed!!.inferType(program)
memoryAddress != null -> InferredTypes.knownFor(BaseDataType.UBYTE)
pointerDereference != null -> pointerDereference!!.inferType(program)
pointerIndexedDeref != null -> pointerIndexedDeref!!.inferType(program)
else -> InferredTypes.unknown() // a multi-target has no 1 particular type
}
}
@@ -685,7 +676,6 @@ data class AssignTarget(
memoryAddress != null -> DirectMemoryRead(memoryAddress!!.addressExpression.copy(), memoryAddress!!.position)
multi != null -> throw FatalAstException("cannot turn a multi-assign into a single source expression")
pointerDereference != null -> pointerDereference!!.copy()
pointerIndexedDeref != null -> pointerIndexedDeref!!.copy()
else -> throw FatalAstException("invalid assignment target")
}
}
@@ -700,12 +690,7 @@ data class AssignTarget(
false
}
identifier != null -> value is IdentifierReference && value.nameInSource == identifier!!.nameInSource
arrayindexed != null -> {
if(value is ArrayIndexedExpression && value.arrayvar.nameInSource == arrayindexed!!.arrayvar.nameInSource)
arrayindexed!!.indexer isSameAs value.indexer
else
false
}
arrayindexed != null -> value is ArrayIndexedExpression && arrayindexed!!.isSameArrayIndexedAs(value)
multi != null -> false
pointerDereference !=null -> {
if(value is PtrDereference) {
@@ -716,11 +701,6 @@ data class AssignTarget(
}
return false
}
pointerIndexedDeref !=null -> {
if(value is PtrIndexedDereference)
return pointerIndexedDeref!!.indexed == value.indexed
return false
}
else -> false
}
}
@@ -736,20 +716,19 @@ data class AssignTarget(
return addr1 != null && addr2 != null && addr1 == addr2
}
this.arrayindexed != null && other.arrayindexed != null -> {
if (this.arrayindexed!!.arrayvar.nameInSource == other.arrayindexed!!.arrayvar.nameInSource) {
if(this.arrayindexed!!.plainarrayvar!=null && this.arrayindexed!!.plainarrayvar?.nameInSource == other.arrayindexed!!.plainarrayvar?.nameInSource) {
val x1 = this.arrayindexed!!.indexer.constIndex()
val x2 = other.arrayindexed!!.indexer.constIndex()
return x1 != null && x2 != null && x1 == x2
}
else if(this.pointerDereference != null && other.pointerDereference != null && this.pointerDereference!!.isSamePointerDeref(other.pointerDereference))
return true
else
return false
}
pointerDereference !=null && other.pointerDereference !=null -> {
return pointerDereference!! isSameAs other.pointerDereference!!
}
pointerIndexedDeref !=null && other.pointerIndexedDeref !=null -> {
return pointerIndexedDeref!! isSameAs other.pointerIndexedDeref!!
}
this.multi != null && other.multi != null -> return this.multi == other.multi
else -> return false
}
@@ -777,14 +756,18 @@ data class AssignTarget(
}
}
arrayIdx != null -> {
val targetStmt = arrayIdx.arrayvar.targetVarDecl()
return if (targetStmt?.type == VarDeclType.MEMORY) {
val addr = targetStmt.value as? NumericLiteral
if (addr != null)
target.isIOAddress(addr.number.toUInt())
else
false
} else false
if(arrayIdx.plainarrayvar!=null) {
val targetStmt = arrayIdx.plainarrayvar!!.targetVarDecl()
return if (targetStmt?.type == VarDeclType.MEMORY) {
val addr = targetStmt.value as? NumericLiteral
if (addr != null)
target.isIOAddress(addr.number.toUInt())
else
false
} else false
}
// can't really tell for the other types... assume false.
return false
}
ident != null -> {
val decl = ident.targetVarDecl() ?: throw FatalAstException("invalid identifier ${ident.nameInSource}")

View File

@@ -106,7 +106,6 @@ abstract class AstWalker {
open fun before(containment: ContainmentCheck, parent: Node): Iterable<IAstModification> = noModifications
open fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> = noModifications
open fun before(deref: PtrDereference, parent: Node): Iterable<IAstModification> = noModifications
open fun before(idxderef: PtrIndexedDereference, parent: Node): Iterable<IAstModification> = noModifications
open fun before(struct: StructDecl, parent: Node): Iterable<IAstModification> = noModifications
open fun before(field: StructFieldRef, parent: Node): Iterable<IAstModification> = noModifications
open fun before(directive: Directive, parent: Node): Iterable<IAstModification> = noModifications
@@ -156,7 +155,6 @@ abstract class AstWalker {
open fun after(containment: ContainmentCheck, parent: Node): Iterable<IAstModification> = noModifications
open fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> = noModifications
open fun after(deref: PtrDereference, parent: Node): Iterable<IAstModification> = noModifications
open fun after(idxderef: PtrIndexedDereference, parent: Node): Iterable<IAstModification> = noModifications
open fun after(struct: StructDecl, parent: Node): Iterable<IAstModification> = noModifications
open fun after(field: StructFieldRef, parent: Node): Iterable<IAstModification> = noModifications
open fun after(directive: Directive, parent: Node): Iterable<IAstModification> = noModifications
@@ -438,7 +436,8 @@ abstract class AstWalker {
fun visit(arrayIndexedExpression: ArrayIndexedExpression, parent: Node) {
track(before(arrayIndexedExpression, parent), arrayIndexedExpression, parent)
arrayIndexedExpression.arrayvar.accept(this, arrayIndexedExpression)
arrayIndexedExpression.plainarrayvar?.accept(this, arrayIndexedExpression)
arrayIndexedExpression.pointerderef?.accept(this, arrayIndexedExpression)
arrayIndexedExpression.indexer.accept(this)
track(after(arrayIndexedExpression, parent), arrayIndexedExpression, parent)
}
@@ -449,7 +448,6 @@ abstract class AstWalker {
assignTarget.identifier?.accept(this, assignTarget)
assignTarget.memoryAddress?.accept(this, assignTarget)
assignTarget.pointerDereference?.accept(this, assignTarget)
assignTarget.pointerIndexedDeref?.accept(this, assignTarget)
assignTarget.multi?.forEach { it.accept(this, assignTarget) }
track(after(assignTarget, parent), assignTarget, parent)
}
@@ -539,11 +537,5 @@ abstract class AstWalker {
deref.identifier.accept(this, deref)
track(after(deref, parent), deref, parent)
}
fun visit(idxderef: PtrIndexedDereference, parent: Node) {
track(before(idxderef, parent), idxderef, parent)
idxderef.indexed.accept(this, idxderef)
track(after(idxderef, parent), idxderef, parent)
}
}

View File

@@ -152,7 +152,8 @@ interface IAstVisitor {
}
fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
arrayIndexedExpression.arrayvar.accept(this)
arrayIndexedExpression.plainarrayvar?.accept(this)
arrayIndexedExpression.pointerderef?.accept(this)
arrayIndexedExpression.indexer.accept(this)
}
@@ -161,7 +162,6 @@ interface IAstVisitor {
assignTarget.identifier?.accept(this)
assignTarget.memoryAddress?.accept(this)
assignTarget.pointerDereference?.accept(this)
assignTarget.pointerIndexedDeref?.accept(this)
assignTarget.multi?.forEach { it.accept(this) }
}
@@ -218,8 +218,4 @@ interface IAstVisitor {
fun visit(deref: PtrDereference) {
deref.identifier.accept(this)
}
fun visit(idxderef: PtrIndexedDereference) {
idxderef.indexed.accept(this)
}
}

View File

@@ -1,6 +1,8 @@
TODO
====
Something changed in array/string/uword indexing codegen? Assem became bigger, rockrunner too.
STRUCTS and TYPED POINTERS
--------------------------
@@ -56,8 +58,8 @@ STRUCTS and TYPED POINTERS
- fix ptr problems in re.p8 and unit tests
- add unit tests for expected AST elements for all syntaxes dealing with pointers, dereference(chain), derefs, and indexing (both as value and assigntargets)
- clean up pointerdereference in the grammar, regarding dealing with final ^^ or not
- Can we now get rid of PtPointerIndexedDeref ? Both for expression (value) as assigntarget? All code for translate(idxderef: PtPointerIndexedDeref) in ExpressionGen?
- why does PtrDereference have both identifier and chain property? what goes where? is the distinction needed?
- why does PtrDereference have both chain and field property? what goes where? is the distinction needed?
- add unit tests for all changes (pointers and structs)
- 6502 codegen: remove checks in checkForPointerTypesOn6502()
- 6502 codegen should warn about writing to initialized struct instances when using romable code, like with arrays "can only be used as read-only in ROMable code"

View File

@@ -2,15 +2,20 @@ main {
struct List {
^^uword s
ubyte n
^^List next
}
sub start() {
^^List l1 = List()
^^List l2 = List()
l1.s[2] = 1
l2.n=10
^^word @shared wptr
^^List l3 = List()
cx16.r0L = l3.next.n
cx16.r1 = l1.s^^
cx16.r0 = l1.s[0]
cx16.r2 = l1^^.s^^
l1.s[0] = 4242
cx16.r1 = l1.s^^
cx16.r0s = wptr[0]
cx16.r1s = wptr^^
wptr[0] = 4242
}
}

View File

@@ -187,7 +187,6 @@ assign_target:
| arrayindexed #ArrayindexedTarget
| directmemory #MemoryTarget
| pointerdereference #PointerDereferenceTarget
| pointerindexedderef #PointerIndexedDerefTarget
| VOID #VoidTarget
;
@@ -225,7 +224,6 @@ expression :
| expression typecast
| if_expression
| pointerdereference
| pointerindexedderef
;
arrayindexed:
@@ -339,9 +337,6 @@ derefchain : singlederef ('.' singlederef)* ;
singlederef : identifier POINTER ;
pointerindexedderef : arrayindexed POINTER ;
branch_stmt : branchcondition EOL? (statement | statement_block) EOL? else_part? ;
branchcondition: 'if_cs' | 'if_cc' | 'if_eq' | 'if_z' | 'if_ne' | 'if_nz' | 'if_pl' | 'if_pos' | 'if_mi' | 'if_neg' | 'if_vs' | 'if_vc' ;

View File

@@ -116,7 +116,6 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
is PtRange -> true
is PtString -> true
is PtPointerDeref -> this.startpointer.isSimple() && this.field==null && this.chain.isEmpty()
is PtPointerIndexedDeref -> this.index.isSimple()
is PtTypeCast -> value.isSimple()
is PtIfExpression -> condition.isSimple() && truevalue.isSimple() && falsevalue.isSimple()
}
@@ -418,16 +417,6 @@ class PtPointerDeref(type: DataType, val chain: List<String>, val field: String?
}
}
class PtPointerIndexedDeref(type: DataType, position: Position) : PtExpression(type, position) {
val variable: PtIdentifier
get() = children[0] as PtIdentifier
val index: PtExpression
get() = children[1] as PtExpression
init {
require(!type.isUndefined)
}
}
// 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)

View File

@@ -193,9 +193,6 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
val field = if(node.field==null) "" else ".${node.field}"
"deref {child} $chain $field ${type(node.type)}"
}
is PtPointerIndexedDeref -> {
"idxderef {child} ${type(node.type)}"
}
}
}

View File

@@ -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 pointerIndexedDeref: PtPointerIndexedDeref?
get() = children.single() as? PtPointerIndexedDeref
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 PtPointerIndexedDeref -> tgt.type
else -> throw AssemblyError("weird target $tgt")
}
}