mirror of
https://github.com/irmen/prog8.git
synced 2026-04-25 12:33:55 +00:00
initial struct and typed pointer support
This commit is contained in:
+704
-567
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,8 @@ enum class BaseDataType {
|
||||
STR, // pass by reference
|
||||
ARRAY, // pass by reference, subtype is the element type
|
||||
ARRAY_SPLITW, // pass by reference, split word layout, subtype is the element type (restricted to word types)
|
||||
POINTER, // typed pointer, subtype is whatever type is pointed to
|
||||
ARRAY_POINTER, // array of pointers (uwords), subtype is whatever type each element points to
|
||||
UNDEFINED;
|
||||
|
||||
|
||||
@@ -50,25 +52,33 @@ val BaseDataType.isIntegerOrBool get() = this in arrayOf(BaseDataType.UBYTE, Bas
|
||||
val BaseDataType.isNumeric get() = this == BaseDataType.FLOAT || this.isInteger
|
||||
val BaseDataType.isNumericOrBool get() = this == BaseDataType.BOOL || this.isNumeric
|
||||
val BaseDataType.isSigned get() = this in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
|
||||
val BaseDataType.isArray get() = this == BaseDataType.ARRAY || this == BaseDataType.ARRAY_SPLITW
|
||||
val BaseDataType.isArray get() = this == BaseDataType.ARRAY || this == BaseDataType.ARRAY_SPLITW || this == BaseDataType.ARRAY_POINTER
|
||||
val BaseDataType.isPointer get() = this == BaseDataType.POINTER
|
||||
val BaseDataType.isPointerArray get() = this == BaseDataType.ARRAY_POINTER
|
||||
val BaseDataType.isSplitWordArray get() = this == BaseDataType.ARRAY_SPLITW
|
||||
val BaseDataType.isIterable get() = this in arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW)
|
||||
val BaseDataType.isIterable get() = this in arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW, BaseDataType.ARRAY_POINTER)
|
||||
val BaseDataType.isPassByRef get() = this.isIterable
|
||||
val BaseDataType.isPassByValue get() = !this.isIterable
|
||||
|
||||
|
||||
class DataType private constructor(val base: BaseDataType, val sub: BaseDataType?) {
|
||||
class DataType private constructor(val base: BaseDataType, val sub: BaseDataType?, val subIdentifier: List<String>?) {
|
||||
|
||||
init {
|
||||
if(base.isArray) {
|
||||
require(sub != null)
|
||||
if(base.isPointerArray) {
|
||||
require(sub!=null || subIdentifier!=null)
|
||||
}
|
||||
else if(base.isArray) {
|
||||
require(sub != null && subIdentifier==null)
|
||||
if(base.isSplitWordArray)
|
||||
require(sub == BaseDataType.UWORD || sub == BaseDataType.WORD)
|
||||
}
|
||||
else if(base==BaseDataType.STR)
|
||||
require(sub==BaseDataType.UBYTE) { "string subtype should be ubyte" }
|
||||
else
|
||||
require(sub == null) { "only string and array base types can have a subtype"}
|
||||
else if(base!=BaseDataType.POINTER)
|
||||
require(sub == null) { "only string, array and pointer base types can have a subtype"}
|
||||
|
||||
require(sub == null || subIdentifier == null) { "subtype and identifier can't both be set" }
|
||||
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
@@ -81,41 +91,49 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
|
||||
companion object {
|
||||
|
||||
val UBYTE = DataType(BaseDataType.UBYTE, null)
|
||||
val BYTE = DataType(BaseDataType.BYTE, null)
|
||||
val UWORD = DataType(BaseDataType.UWORD, null)
|
||||
val WORD = DataType(BaseDataType.WORD, null)
|
||||
val LONG = DataType(BaseDataType.LONG, null)
|
||||
val FLOAT = DataType(BaseDataType.FLOAT, null)
|
||||
val BOOL = DataType(BaseDataType.BOOL, null)
|
||||
val STR = DataType(BaseDataType.STR, BaseDataType.UBYTE)
|
||||
val UNDEFINED = DataType(BaseDataType.UNDEFINED, null)
|
||||
val UBYTE = DataType(BaseDataType.UBYTE, null, null)
|
||||
val BYTE = DataType(BaseDataType.BYTE, null, null)
|
||||
val UWORD = DataType(BaseDataType.UWORD, null, null)
|
||||
val WORD = DataType(BaseDataType.WORD, null, null)
|
||||
val LONG = DataType(BaseDataType.LONG, null, null)
|
||||
val FLOAT = DataType(BaseDataType.FLOAT, null, null)
|
||||
val BOOL = DataType(BaseDataType.BOOL, null, null)
|
||||
val STR = DataType(BaseDataType.STR, BaseDataType.UBYTE, null)
|
||||
val UNDEFINED = DataType(BaseDataType.UNDEFINED, null, null)
|
||||
|
||||
private val simpletypes = mapOf(
|
||||
BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null),
|
||||
BaseDataType.BYTE to DataType(BaseDataType.BYTE, null),
|
||||
BaseDataType.UWORD to DataType(BaseDataType.UWORD, null),
|
||||
BaseDataType.WORD to DataType(BaseDataType.WORD, null),
|
||||
BaseDataType.LONG to DataType(BaseDataType.LONG, null),
|
||||
BaseDataType.FLOAT to DataType(BaseDataType.FLOAT, null),
|
||||
BaseDataType.BOOL to DataType(BaseDataType.BOOL, null),
|
||||
BaseDataType.STR to DataType(BaseDataType.STR, BaseDataType.UBYTE),
|
||||
BaseDataType.UNDEFINED to DataType(BaseDataType.UNDEFINED, null)
|
||||
BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null, null),
|
||||
BaseDataType.BYTE to DataType(BaseDataType.BYTE, null, null),
|
||||
BaseDataType.UWORD to DataType(BaseDataType.UWORD, null, null),
|
||||
BaseDataType.WORD to DataType(BaseDataType.WORD, null, null),
|
||||
BaseDataType.LONG to DataType(BaseDataType.LONG, null, null),
|
||||
BaseDataType.FLOAT to DataType(BaseDataType.FLOAT, null, null),
|
||||
BaseDataType.BOOL to DataType(BaseDataType.BOOL, null, null),
|
||||
BaseDataType.STR to DataType(BaseDataType.STR, BaseDataType.UBYTE, null),
|
||||
BaseDataType.UNDEFINED to DataType(BaseDataType.UNDEFINED, null, null)
|
||||
)
|
||||
|
||||
fun forDt(dt: BaseDataType) = simpletypes.getValue(dt)
|
||||
|
||||
fun arrayFor(elementDt: BaseDataType, splitwordarray: Boolean=true): DataType {
|
||||
require(!elementDt.isPointer) { "use other array constructor for arrays of pointers" }
|
||||
val actualElementDt = if(elementDt==BaseDataType.STR) BaseDataType.UWORD else elementDt // array of strings is actually just an array of UWORD pointers
|
||||
return if(splitwordarray && actualElementDt.isWord)
|
||||
DataType(BaseDataType.ARRAY_SPLITW, actualElementDt)
|
||||
DataType(BaseDataType.ARRAY_SPLITW, actualElementDt, null)
|
||||
else {
|
||||
if(actualElementDt.isNumericOrBool && actualElementDt != BaseDataType.LONG)
|
||||
DataType(BaseDataType.ARRAY, actualElementDt)
|
||||
DataType(BaseDataType.ARRAY, actualElementDt, null)
|
||||
else
|
||||
throw NoSuchElementException("invalid element dt $elementDt")
|
||||
}
|
||||
}
|
||||
|
||||
fun arrayOfPointersTo(sub: BaseDataType?, subIdentifier: List<String>?): DataType =
|
||||
DataType(BaseDataType.ARRAY_POINTER, sub, subIdentifier)
|
||||
|
||||
fun pointer(base: BaseDataType): DataType = DataType(BaseDataType.POINTER, base, null)
|
||||
|
||||
fun pointer(scopedIdentifier: List<String>): DataType = DataType(BaseDataType.POINTER, null, scopedIdentifier)
|
||||
}
|
||||
|
||||
fun elementToArray(splitwords: Boolean = true): DataType {
|
||||
@@ -124,7 +142,9 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
}
|
||||
|
||||
fun elementType(): DataType =
|
||||
if(base.isArray || base==BaseDataType.STR)
|
||||
if(isPointerArray)
|
||||
DataType(BaseDataType.POINTER, sub, subIdentifier)
|
||||
else if(base.isArray || base==BaseDataType.STR)
|
||||
forDt(sub!!)
|
||||
else
|
||||
throw IllegalArgumentException("not an array")
|
||||
@@ -148,6 +168,12 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
BaseDataType.POINTER -> {
|
||||
if(sub!=null) "^${sub.name.lowercase()}" else "^${subIdentifier!!.joinToString(".")}"
|
||||
}
|
||||
BaseDataType.ARRAY_POINTER -> {
|
||||
if(sub!=null) "^${sub.name.lowercase()}[] (split)" else "^${subIdentifier!!.joinToString(".")}[] (split)"
|
||||
}
|
||||
else -> base.name.lowercase()
|
||||
}
|
||||
|
||||
@@ -160,6 +186,12 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
BaseDataType.LONG -> "long"
|
||||
BaseDataType.FLOAT -> "float"
|
||||
BaseDataType.STR -> "str"
|
||||
BaseDataType.POINTER -> {
|
||||
if(sub!=null) "^${sub.name.lowercase()}" else "^${subIdentifier!!.joinToString(".")}"
|
||||
}
|
||||
BaseDataType.ARRAY_POINTER -> {
|
||||
if(sub!=null) "^${sub.name.lowercase()}[" else "^${subIdentifier!!.joinToString(".")}["
|
||||
}
|
||||
BaseDataType.ARRAY -> {
|
||||
when(sub) {
|
||||
BaseDataType.UBYTE -> "ubyte["
|
||||
@@ -193,12 +225,20 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
BaseDataType.FLOAT -> targetType.base in arrayOf(BaseDataType.FLOAT)
|
||||
BaseDataType.STR -> targetType.base in arrayOf(BaseDataType.STR, BaseDataType.UWORD)
|
||||
BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW -> targetType.base in arrayOf(BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW) && targetType.sub == sub
|
||||
BaseDataType.POINTER, BaseDataType.ARRAY_POINTER -> {
|
||||
when {
|
||||
targetType.base == BaseDataType.UWORD || targetType.base == BaseDataType.LONG -> true
|
||||
targetType.isPointer -> this.isUnsignedWord
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
BaseDataType.UNDEFINED -> false
|
||||
}
|
||||
|
||||
fun largerSizeThan(other: DataType): Boolean = base.largerSizeThan(other.base)
|
||||
fun equalsSize(other: DataType): Boolean = base.equalsSize(other.base)
|
||||
|
||||
val isBasic = sub==null && subIdentifier==null
|
||||
val isUndefined = base == BaseDataType.UNDEFINED
|
||||
val isByte = base.isByte
|
||||
val isUnsignedByte = base == BaseDataType.UBYTE
|
||||
@@ -214,6 +254,8 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
val isSigned = base.isSigned
|
||||
val isUnsigned = !base.isSigned
|
||||
val isArray = base.isArray
|
||||
val isPointer = base.isPointer
|
||||
val isPointerArray = base.isPointerArray
|
||||
val isBoolArray = base.isArray && sub == BaseDataType.BOOL
|
||||
val isByteArray = base.isArray && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE)
|
||||
val isUnsignedByteArray = base.isArray && sub == BaseDataType.UBYTE
|
||||
|
||||
@@ -7,7 +7,9 @@ import prog8.code.core.IMemSizer
|
||||
internal class NormalMemSizer(val floatsize: Int): IMemSizer {
|
||||
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray) {
|
||||
if(dt.isPointerArray)
|
||||
return 2 * numElements!! // array of pointers is just array of uwords
|
||||
else if(dt.isArray) {
|
||||
if(numElements==null) return 2 // treat it as a pointer size
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
|
||||
@@ -26,6 +28,7 @@ internal class NormalMemSizer(val floatsize: Int): IMemSizer {
|
||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||
dt.isFloat -> floatsize * (numElements ?: 1)
|
||||
dt.isLong -> throw IllegalArgumentException("long can not yet be put into memory")
|
||||
dt.isPointer -> 2 // pointer is just a uword
|
||||
dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> 2 * (numElements ?: 1)
|
||||
}
|
||||
|
||||
@@ -90,7 +90,9 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Nor
|
||||
}
|
||||
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray) {
|
||||
if(dt.isPointerArray)
|
||||
return 2 * numElements!! // array of pointers is just array of uwords
|
||||
else if(dt.isArray) {
|
||||
if(numElements==null) return 2 // treat it as a pointer size
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.UBYTE, BaseDataType.BYTE -> numElements
|
||||
@@ -109,6 +111,7 @@ class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by Nor
|
||||
dt.isByteOrBool -> 1 * (numElements ?: 1)
|
||||
dt.isFloat -> FLOAT_MEM_SIZE * (numElements ?: 1)
|
||||
dt.isLong -> throw IllegalArgumentException("long can not yet be put into memory")
|
||||
dt.isPointer -> 2 // pointer is just a uword
|
||||
dt.isUndefined -> throw IllegalArgumentException("undefined has no memory size")
|
||||
else -> 2 * (numElements ?: 1)
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
|
||||
is PtLabel -> if(!node.name.startsWith(GENERATED_LABEL_PREFIX)) node.name = "p8l_${node.name}" // don't prefix autogenerated labels
|
||||
is PtConstant -> node.name = "p8c_${node.name}"
|
||||
is PtVariable, is PtMemMapped, is PtSubroutineParameter -> node.name = "p8v_${node.name}"
|
||||
is PtStructDecl -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +178,7 @@ private fun PtVariable.prefix(parent: PtNode, st: SymbolTable): PtVariable {
|
||||
else {
|
||||
val newAddr = PtAddressOf(elt.position)
|
||||
newAddr.children.add(elt.identifier.prefix(newAddr, st))
|
||||
if(elt.arrayIndexExpr!=null)
|
||||
if (elt.arrayIndexExpr != null)
|
||||
newAddr.children.add(elt.arrayIndexExpr!!)
|
||||
newAddr.parent = arrayValue
|
||||
newValue.add(newAddr)
|
||||
@@ -638,7 +639,7 @@ class AsmGen6502Internal (
|
||||
is PtDefer -> throw AssemblyError("defer should have been transformed")
|
||||
is PtNodeGroup -> stmt.children.forEach { translate(it) }
|
||||
is PtJmpTable -> translate(stmt)
|
||||
is PtNop -> {}
|
||||
is PtNop, is PtStructDecl -> {}
|
||||
else -> throw AssemblyError("missing asm translation for $stmt")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
// use a BIT instruction to test for bit 7 or 6 set/clear
|
||||
val (testBitSet, variable, bitmask) = useBIT
|
||||
return translateIfBIT(stmt, jumpAfterIf, testBitSet, variable, bitmask)
|
||||
return
|
||||
}
|
||||
|
||||
val rightDt = compareCond.right.type
|
||||
@@ -1596,9 +1595,9 @@ _jump jmp (${target.asmLabel})
|
||||
}
|
||||
}
|
||||
is PtAddressOf -> {
|
||||
if(left.isFromArrayElement)
|
||||
if(left.isFromArrayElement) {
|
||||
fallbackTranslateForSimpleCondition(stmt)
|
||||
else {
|
||||
} else {
|
||||
val varname = if(left.identifier.type.isSplitWordArray) {
|
||||
if(left.isMsbForSplitArray) left.identifier.name+"_msb" else left.identifier.name+"_lsb"
|
||||
} else {
|
||||
@@ -1648,9 +1647,9 @@ _jump jmp (${target.asmLabel})
|
||||
}
|
||||
}
|
||||
is PtAddressOf -> {
|
||||
if(left.isFromArrayElement)
|
||||
if(left.isFromArrayElement) {
|
||||
fallbackTranslateForSimpleCondition(stmt)
|
||||
else {
|
||||
} else {
|
||||
val varname = if(left.identifier.type.isSplitWordArray) {
|
||||
if(left.isMsbForSplitArray) left.identifier.name+"_msb" else left.identifier.name+"_lsb"
|
||||
} else {
|
||||
|
||||
@@ -709,6 +709,10 @@ internal class ProgramAndVarsGen(
|
||||
val numbytes = compTarget.memorySize(variable.dt, variable.length!!)
|
||||
asmgen.out("${variable.name}\t.fill $numbytes")
|
||||
}
|
||||
dt.isPointer -> asmgen.out("${variable.name}\t.word ?") // a pointer is just an uword address
|
||||
dt.isPointerArray -> {
|
||||
TODO("pointers are not supported yet")
|
||||
}
|
||||
else -> {
|
||||
throw AssemblyError("weird dt")
|
||||
}
|
||||
|
||||
@@ -440,9 +440,9 @@ internal class AssignmentAsmGen(
|
||||
is PtAddressOf -> {
|
||||
val arrayDt = value.identifier.type
|
||||
val sourceName =
|
||||
if(value.isMsbForSplitArray)
|
||||
if (value.isMsbForSplitArray)
|
||||
asmgen.asmSymbolName(value.identifier) + "_msb"
|
||||
else if(arrayDt.isSplitWordArray)
|
||||
else if (arrayDt.isSplitWordArray)
|
||||
asmgen.asmSymbolName(value.identifier) + "_lsb" // the _lsb split array comes first in memory
|
||||
else
|
||||
asmgen.asmSymbolName(value.identifier)
|
||||
@@ -1382,10 +1382,10 @@ internal class AssignmentAsmGen(
|
||||
|
||||
when (right) {
|
||||
is PtAddressOf -> {
|
||||
var symbol = asmgen.asmVariableName(right.identifier)
|
||||
if(right.isFromArrayElement) {
|
||||
TODO("address-of array element $symbol at ${right.position}")
|
||||
TODO("address-of array element at ${right.position}")
|
||||
} else {
|
||||
var symbol = asmgen.asmVariableName(right.identifier)
|
||||
if(right.identifier.type.isSplitWordArray) {
|
||||
symbol = if(right.isMsbForSplitArray) symbol+"_msb" else symbol+"_lsb"
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@ import prog8.code.core.*
|
||||
|
||||
internal object DummyMemsizer : IMemSizer {
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray) {
|
||||
if(dt.isPointerArray)
|
||||
return 2 * numElements!!
|
||||
else if(dt.isArray) {
|
||||
require(numElements != null)
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
|
||||
|
||||
@@ -98,6 +98,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun translate(ifExpr: PtIfExpression): ExpressionCodeResult {
|
||||
|
||||
if((ifExpr.condition as? PtPrefix)?.operator=="not")
|
||||
@@ -166,37 +167,38 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
|
||||
private fun translate(expr: PtAddressOf): ExpressionCodeResult {
|
||||
val vmDt = irType(expr.type)
|
||||
val symbol = expr.identifier.name
|
||||
// note: LOAD <symbol> gets you the address of the symbol, whereas LOADM <symbol> would get you the value stored at that location
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val resultRegister = codeGen.registers.next(vmDt)
|
||||
val identifier = expr.identifier
|
||||
|
||||
fun loadAddressOfArrayLabel(reg: Int) {
|
||||
if (expr.isMsbForSplitArray) {
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = symbol + "_msb"), null)
|
||||
} else if (expr.identifier.type.isSplitWordArray) {
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = identifier.name + "_msb"), null)
|
||||
} else if (identifier.type.isSplitWordArray) {
|
||||
// the _lsb split array comes first in memory
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = symbol + "_lsb"), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = identifier.name + "_lsb"), null)
|
||||
} else
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = symbol), null)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, vmDt, reg1 = reg, labelSymbol = identifier.name), null)
|
||||
}
|
||||
|
||||
val resultRegister = codeGen.registers.next(vmDt)
|
||||
|
||||
if(expr.isFromArrayElement) {
|
||||
val indexTr = translateExpression(expr.arrayIndexExpr!!)
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
val indexWordReg = codeGen.registers.next(IRDataType.WORD)
|
||||
addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=indexWordReg, reg2=indexTr.resultReg), null)
|
||||
if(expr.identifier.type.isUnsignedWord) {
|
||||
if(identifier.type.isUnsignedWord) {
|
||||
require(!expr.isMsbForSplitArray)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = symbol)
|
||||
it += IRInstruction(Opcode.LOADM, vmDt, reg1 = resultRegister, labelSymbol = identifier.name)
|
||||
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=resultRegister, reg2=indexWordReg)
|
||||
}
|
||||
} else {
|
||||
val eltSize = codeGen.program.memsizer.memorySize(expr.identifier.type, 1)
|
||||
val eltSize = codeGen.program.memsizer.memorySize(identifier.type, 1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
loadAddressOfArrayLabel(resultRegister)
|
||||
if(eltSize>1 && !expr.identifier.type.isSplitWordArray) {
|
||||
if(eltSize>1 && !identifier.type.isSplitWordArray) {
|
||||
it += IRInstruction(Opcode.MUL, IRDataType.WORD, reg1=indexWordReg, immediate = eltSize)
|
||||
}
|
||||
it += IRInstruction(Opcode.ADDR, IRDataType.WORD, reg1=resultRegister, reg2=indexWordReg)
|
||||
@@ -506,6 +508,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.WORD)
|
||||
addInstr(result, IRInstruction(Opcode.FTOUW, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
|
||||
}
|
||||
BaseDataType.POINTER -> {
|
||||
actualResultReg2 = tr.resultReg
|
||||
}
|
||||
else -> throw AssemblyError("weird cast value type")
|
||||
}
|
||||
}
|
||||
@@ -549,6 +554,14 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
else -> throw AssemblyError("weird cast value type")
|
||||
}
|
||||
}
|
||||
BaseDataType.POINTER -> {
|
||||
require(valueDt.isUnsignedWord || valueDt.isPointer)
|
||||
actualResultReg2 = tr.resultReg
|
||||
// no further conversion required, pointers are all just uwords
|
||||
}
|
||||
BaseDataType.ARRAY_POINTER -> {
|
||||
TODO("typecast to array of pointers $valueDt -> ${cast.type}")
|
||||
}
|
||||
else -> throw AssemblyError("weird cast type")
|
||||
}
|
||||
|
||||
|
||||
@@ -259,6 +259,7 @@ class IRCodeGen(
|
||||
is PtDefer -> throw AssemblyError("should have been transformed")
|
||||
is PtString -> throw AssemblyError("should not occur as separate statement node ${node.position}")
|
||||
is PtSub -> throw AssemblyError("nested subroutines should have been flattened ${node.position}")
|
||||
is PtStructDecl -> emptyList()
|
||||
else -> TODO("missing codegen for $node")
|
||||
}
|
||||
|
||||
@@ -1885,6 +1886,7 @@ class IRCodeGen(
|
||||
}
|
||||
}
|
||||
}
|
||||
is PtStructDecl -> { /* do nothing */ }
|
||||
else -> TODO("weird block child node $child")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ import prog8.code.core.*
|
||||
|
||||
internal object DummyMemsizer : IMemSizer {
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray || dt.isSplitWordArray) {
|
||||
if(dt.isPointerArray)
|
||||
return 2 * numElements!!
|
||||
else if(dt.isArray || dt.isSplitWordArray) {
|
||||
require(numElements!=null)
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
|
||||
|
||||
@@ -390,7 +390,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
val loopvar = forLoop.loopVar.targetVarDecl() ?: return noModifications
|
||||
|
||||
val stepLiteral = iterableRange.step as? NumericLiteral
|
||||
require(loopvar.datatype.sub == null)
|
||||
require(loopvar.datatype.isBasic)
|
||||
val loopvarSimpleDt = loopvar.datatype.base
|
||||
when(loopvarSimpleDt) {
|
||||
BaseDataType.UBYTE -> {
|
||||
|
||||
@@ -18,8 +18,8 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
|
||||
// try to statically convert a literal value into one of the desired type
|
||||
val literal = typecast.expression as? NumericLiteral
|
||||
if (literal != null) {
|
||||
val newLiteral = literal.cast(typecast.type, typecast.implicit)
|
||||
if (literal != null && typecast.type.isBasic) {
|
||||
val newLiteral = literal.cast(typecast.type.base, typecast.implicit)
|
||||
if (newLiteral.isValid && newLiteral.valueOrZero() !== literal) {
|
||||
mods += IAstModification.ReplaceNode(typecast, newLiteral.valueOrZero(), parent)
|
||||
}
|
||||
@@ -33,7 +33,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
mods += IAstModification.ReplaceNode(typecast.expression, subTypecast.expression, typecast)
|
||||
}
|
||||
} else {
|
||||
if (typecast.expression.inferType(program) issimpletype typecast.type) {
|
||||
if (typecast.expression.inferType(program) istype typecast.type) {
|
||||
// remove duplicate cast
|
||||
mods += IAstModification.ReplaceNode(typecast, typecast.expression, parent)
|
||||
}
|
||||
@@ -499,7 +499,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
}
|
||||
else if (valueDt issimpletype BaseDataType.BYTE) {
|
||||
// useless lsb() of byte value, but as lsb() returns unsigned, we have to cast now.
|
||||
val cast = TypecastExpression(arg.expression, BaseDataType.UBYTE, true, arg.position)
|
||||
val cast = TypecastExpression(arg.expression, DataType.UBYTE, true, arg.position)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
|
||||
}
|
||||
} else {
|
||||
@@ -516,7 +516,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
}
|
||||
else if (argDt issimpletype BaseDataType.BYTE) {
|
||||
// useless lsb() of byte value, but as lsb() returns unsigned, we have to cast now.
|
||||
val cast = TypecastExpression(arg, BaseDataType.UBYTE, true, arg.position)
|
||||
val cast = TypecastExpression(arg, DataType.UBYTE, true, arg.position)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
|
||||
}
|
||||
}
|
||||
@@ -555,7 +555,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
if(functionCallExpr.target.nameInSource == listOf("mkword")) {
|
||||
if(functionCallExpr.args[0].constValue(program)?.number==0.0) {
|
||||
// just cast the lsb to uword
|
||||
val cast = TypecastExpression(functionCallExpr.args[1], BaseDataType.UWORD, true, functionCallExpr.position)
|
||||
val cast = TypecastExpression(functionCallExpr.args[1], DataType.UWORD, true, functionCallExpr.position)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
|
||||
}
|
||||
}
|
||||
@@ -711,9 +711,9 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
// just use: msb(value) as type
|
||||
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
return if(leftDt.isSignedWord)
|
||||
TypecastExpression(msb, BaseDataType.BYTE, true, expr.position)
|
||||
TypecastExpression(msb, DataType.BYTE, true, expr.position)
|
||||
else
|
||||
TypecastExpression(msb, BaseDataType.UWORD, true, expr.position)
|
||||
TypecastExpression(msb, DataType.UWORD, true, expr.position)
|
||||
}
|
||||
else -> return null
|
||||
}
|
||||
@@ -839,14 +839,14 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
// shift left by 8 bits is just a byte operation: mkword(lsb(X), 0)
|
||||
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
val mkword = FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(lsb, NumericLiteral(BaseDataType.UBYTE, 0.0, expr.position)), expr.position)
|
||||
return TypecastExpression(mkword, BaseDataType.WORD, true, expr.position)
|
||||
return TypecastExpression(mkword, DataType.WORD, true, expr.position)
|
||||
}
|
||||
else if (amount > 8) {
|
||||
// same as above but with residual shifts.
|
||||
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
val shifted = BinaryExpression(lsb, "<<", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position)
|
||||
val mkword = FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(shifted, NumericLiteral.optimalInteger(0, expr.position)), expr.position)
|
||||
return TypecastExpression(mkword, BaseDataType.WORD, true, expr.position)
|
||||
return TypecastExpression(mkword, DataType.WORD, true, expr.position)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
@@ -887,12 +887,12 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
else if(amount==8) {
|
||||
// shift right by 8 bits is just a byte operation: msb(X) as uword
|
||||
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
return TypecastExpression(msb, BaseDataType.UWORD, true, expr.position)
|
||||
return TypecastExpression(msb, DataType.UWORD, true, expr.position)
|
||||
}
|
||||
else if (amount > 8) {
|
||||
// same as above but with residual shifts.
|
||||
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
return TypecastExpression(BinaryExpression(msb, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position), BaseDataType.UWORD, true, expr.position)
|
||||
return TypecastExpression(BinaryExpression(msb, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position), DataType.UWORD, true, expr.position)
|
||||
}
|
||||
}
|
||||
BaseDataType.WORD -> {
|
||||
@@ -903,12 +903,12 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
else if(amount == 8) {
|
||||
// shift right by 8 bits is just a byte operation: msb(X) as byte (will get converted to word later)
|
||||
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
return TypecastExpression(msb, BaseDataType.BYTE, true, expr.position)
|
||||
return TypecastExpression(msb, DataType.BYTE, true, expr.position)
|
||||
}
|
||||
else if(amount > 8) {
|
||||
// same as above but with residual shifts. Take care to do signed shift.
|
||||
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||
val signed = TypecastExpression(msb, BaseDataType.BYTE, true, expr.position)
|
||||
val signed = TypecastExpression(msb, DataType.BYTE, true, expr.position)
|
||||
return BinaryExpression(signed, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,6 +115,12 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
|
||||
else -> NumericLiteral(BaseDataType.UBYTE, program.memsizer.memorySize(dt.getOrUndef(), null).toDouble(), position)
|
||||
}
|
||||
} else {
|
||||
// the argument could refer to a struct declaration
|
||||
val struct = (args[0] as? IdentifierReference)?.targetStructDecl()
|
||||
if(struct!=null) {
|
||||
val size = struct.memsize(program.memsizer)
|
||||
return NumericLiteral(BaseDataType.UBYTE, size.toDouble(), position)
|
||||
}
|
||||
throw SyntaxError("sizeof invalid argument type", position)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,6 +297,7 @@ internal class AstChecker(private val program: Program,
|
||||
is Directive,
|
||||
is Label,
|
||||
is VarDecl,
|
||||
is StructDecl,
|
||||
is InlineAssembly,
|
||||
is IStatementContainer -> true
|
||||
is Assignment -> {
|
||||
@@ -1336,10 +1337,16 @@ internal class AstChecker(private val program: Program,
|
||||
errors.err("this expression doesn't return a value", typecast.expression.position)
|
||||
|
||||
if(typecast.expression is NumericLiteral) {
|
||||
val castResult = (typecast.expression as NumericLiteral).cast(typecast.type, typecast.implicit)
|
||||
if(castResult.isValid)
|
||||
throw FatalAstException("cast should have been performed in const eval already")
|
||||
errors.err(castResult.whyFailed!!, typecast.expression.position)
|
||||
if(typecast.type.isBasic) {
|
||||
val castResult = (typecast.expression as NumericLiteral).cast(typecast.type.base, typecast.implicit)
|
||||
if (castResult.isValid)
|
||||
throw FatalAstException("cast should have been performed in const eval already")
|
||||
errors.err(castResult.whyFailed!!, typecast.expression.position)
|
||||
} else if (typecast.type.isPointer) {
|
||||
if(!(typecast.expression.inferType(program) istype DataType.UWORD))
|
||||
errors.err("can only cast uword to pointer", typecast.position)
|
||||
} else
|
||||
errors.err("invalid type cast", typecast.position)
|
||||
}
|
||||
|
||||
super.visit(typecast)
|
||||
@@ -1771,6 +1778,12 @@ internal class AstChecker(private val program: Program,
|
||||
errors.err("%asm containing IR code cannot be translated to 6502 assembly", inlineAssembly.position)
|
||||
}
|
||||
|
||||
override fun visit(struct: StructDecl) {
|
||||
val uniqueFields = struct.members.map { it.second }.toSet()
|
||||
if(uniqueFields.size!=struct.members.size)
|
||||
errors.err("duplicate field names in struct", struct.position)
|
||||
}
|
||||
|
||||
private fun checkLongType(expression: Expression) {
|
||||
if(expression.inferType(program) issimpletype BaseDataType.LONG) {
|
||||
if((expression.parent as? VarDecl)?.type!=VarDeclType.CONST) {
|
||||
@@ -1955,6 +1968,9 @@ internal class AstChecker(private val program: Program,
|
||||
targetDt.isArray -> {
|
||||
return checkValueTypeAndRange(targetDt.elementType(), value)
|
||||
}
|
||||
targetDt.isPointer -> {
|
||||
return value.type==BaseDataType.UWORD
|
||||
}
|
||||
else -> return err("type of value ${value.type.toString().lowercase()} doesn't match target $targetDt")
|
||||
}
|
||||
return true
|
||||
@@ -1966,9 +1982,9 @@ internal class AstChecker(private val program: Program,
|
||||
is NumericLiteral -> it.number.toInt()
|
||||
is AddressOf -> it.identifier.nameInSource.hashCode() and 0xffff
|
||||
is IdentifierReference -> it.nameInSource.hashCode() and 0xffff
|
||||
is TypecastExpression -> {
|
||||
is TypecastExpression if it.type.isBasic -> {
|
||||
val constVal = it.expression.constValue(program)
|
||||
val cast = constVal?.cast(it.type, true)
|
||||
val cast = constVal?.cast(it.type.base, true)
|
||||
if(cast==null || !cast.isValid)
|
||||
-9999999
|
||||
else
|
||||
@@ -2051,6 +2067,13 @@ internal class AstChecker(private val program: Program,
|
||||
else if(targetDatatype.isUnsignedWord && sourceDatatype.isPassByRef) {
|
||||
// this is allowed: a pass-by-reference datatype into an uword (pointer value).
|
||||
}
|
||||
else if (targetDatatype.isPointer) {
|
||||
if(sourceDatatype.isPointer) {
|
||||
if(sourceDatatype!=targetDatatype)
|
||||
errors.err("cannot assign different pointer type", position)
|
||||
} else if(!sourceDatatype.isUnsignedWord)
|
||||
errors.err("can only assign uword or correct pointer type to a pointer", position)
|
||||
}
|
||||
else if(targetDatatype.isString && sourceDatatype.isUnsignedWord)
|
||||
errors.err("can't assign uword to str. If the source is a string pointer and you actually want to overwrite the target string, use an explicit strings.copy(src,tgt) instead.", position)
|
||||
else
|
||||
|
||||
@@ -9,7 +9,6 @@ import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.core.isByte
|
||||
import prog8.code.core.isWord
|
||||
|
||||
|
||||
@@ -29,11 +28,11 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
if(typecast.type==sourceDt.base)
|
||||
if(typecast.type==sourceDt)
|
||||
return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
|
||||
|
||||
if(sourceDt.isPassByRef) {
|
||||
if(typecast.type == BaseDataType.UWORD) {
|
||||
if(typecast.type.isUnsignedWord) {
|
||||
val identifier = typecast.expression as? IdentifierReference
|
||||
if(identifier!=null) {
|
||||
return if(identifier.isSubroutineParameter()) {
|
||||
|
||||
@@ -5,10 +5,7 @@ import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.ComparisonOperators
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
internal class CodeDesugarer(val program: Program, private val errors: IErrorReporter) : AstWalker() {
|
||||
@@ -207,7 +204,7 @@ _after:
|
||||
val indexExpr = arrayIndexedExpression.indexer.indexExpr
|
||||
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl()
|
||||
if(arrayVar!=null && arrayVar.datatype.isUnsignedWord) {
|
||||
val wordIndex = TypecastExpression(indexExpr, BaseDataType.UWORD, true, indexExpr.position)
|
||||
val wordIndex = TypecastExpression(indexExpr, DataType.UWORD, true, indexExpr.position)
|
||||
val address = BinaryExpression(arrayIndexedExpression.arrayvar.copy(), "+", wordIndex, arrayIndexedExpression.position)
|
||||
return if(parent is AssignTarget) {
|
||||
// assignment to array
|
||||
@@ -281,7 +278,9 @@ _after:
|
||||
if(addressOf!=null && offset==1) {
|
||||
val variable = addressOf.identifier.targetVarDecl()
|
||||
if(variable!=null && variable.datatype.isWord) {
|
||||
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), memread.position), mutableListOf(addressOf.identifier), memread.position)
|
||||
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), memread.position), mutableListOf(
|
||||
addressOf.identifier
|
||||
), memread.position)
|
||||
return listOf(IAstModification.ReplaceNode(memread, msb, parent))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,8 +74,10 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
}
|
||||
is UntilLoop -> throw FatalAstException("until loops must have been converted to jumps")
|
||||
is VarDecl -> transform(statement)
|
||||
is StructDecl -> transform(statement)
|
||||
is When -> transform(statement)
|
||||
is WhileLoop -> throw FatalAstException("while loops must have been converted to jumps")
|
||||
is StructFieldRef -> throw FatalAstException("should not occur as part of the actual AST")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,14 +182,12 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
|
||||
private fun transform(srcTarget: AssignTarget): PtAssignTarget {
|
||||
val target = PtAssignTarget(srcTarget.void, srcTarget.position)
|
||||
if(srcTarget.identifier!=null)
|
||||
target.add(transform(srcTarget.identifier!!))
|
||||
else if(srcTarget.arrayindexed!=null)
|
||||
target.add(transform(srcTarget.arrayindexed!!))
|
||||
else if(srcTarget.memoryAddress!=null)
|
||||
target.add(transform(srcTarget.memoryAddress!!))
|
||||
else if(!srcTarget.void)
|
||||
throw FatalAstException("invalid AssignTarget")
|
||||
when {
|
||||
srcTarget.identifier!=null -> target.add(transform(srcTarget.identifier!!))
|
||||
srcTarget.arrayindexed!=null -> target.add(transform(srcTarget.arrayindexed!!))
|
||||
srcTarget.memoryAddress!=null -> target.add(transform(srcTarget.memoryAddress!!))
|
||||
!srcTarget.void -> throw FatalAstException("invalid AssignTarget")
|
||||
}
|
||||
return target
|
||||
}
|
||||
|
||||
@@ -588,6 +588,10 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
}
|
||||
}
|
||||
|
||||
private fun transform(struct: StructDecl): PtStructDecl {
|
||||
return PtStructDecl(struct.name, struct.members, struct.position)
|
||||
}
|
||||
|
||||
private fun transform(srcWhen: When): PtWhen {
|
||||
val w = PtWhen(srcWhen.position)
|
||||
w.add(transformExpression(srcWhen.condition))
|
||||
@@ -616,7 +620,7 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
private fun transform(src: AddressOf): PtAddressOf {
|
||||
val addr = PtAddressOf(src.position, src.msb)
|
||||
addr.add(transform(src.identifier))
|
||||
if(src.arrayIndex!=null)
|
||||
if (src.arrayIndex != null)
|
||||
addr.add(transformExpression(src.arrayIndex!!.indexExpr))
|
||||
return addr
|
||||
}
|
||||
@@ -669,14 +673,14 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
expr.add(high)
|
||||
} else {
|
||||
val low = PtBinaryExpression("<=", DataType.BOOL, srcCheck.position)
|
||||
val lowFloat = PtTypeCast(BaseDataType.FLOAT, range.from.position)
|
||||
val lowFloat = PtTypeCast(DataType.FLOAT, range.from.position)
|
||||
lowFloat.add(transformExpression(range.from))
|
||||
low.add(lowFloat)
|
||||
low.add(x1)
|
||||
expr.add(low)
|
||||
val high = PtBinaryExpression("<=", DataType.BOOL, srcCheck.position)
|
||||
high.add(x2)
|
||||
val highFLoat = PtTypeCast(BaseDataType.FLOAT, range.to.position)
|
||||
val highFLoat = PtTypeCast(DataType.FLOAT, range.to.position)
|
||||
highFLoat.add(transformExpression(range.to))
|
||||
high.add(highFLoat)
|
||||
expr.add(high)
|
||||
|
||||
@@ -67,7 +67,7 @@ internal class StatementReorderer(
|
||||
if (!canSkipInitializationWith0(decl)) {
|
||||
// Add assignment to initialize with zero
|
||||
val identifier = IdentifierReference(listOf(decl.name), decl.position)
|
||||
val assignzero = Assignment(AssignTarget(identifier, null, null, null, false, decl.position),
|
||||
val assignzero = Assignment(AssignTarget(identifier, null, null,null, false, decl.position),
|
||||
decl.zeroElementValue(), AssignmentOrigin.VARINIT, decl.position)
|
||||
return listOf(IAstModification.InsertAfter(
|
||||
decl, assignzero, parent as IStatementContainer
|
||||
@@ -99,7 +99,7 @@ internal class StatementReorderer(
|
||||
if(target!=null && target.isArray) {
|
||||
val pos = decl.value!!.position
|
||||
val identifier = IdentifierReference(listOf(decl.name), pos)
|
||||
val assign = Assignment(AssignTarget(identifier, null, null, null, false, pos),
|
||||
val assign = Assignment(AssignTarget(identifier, null, null,null, false, pos),
|
||||
decl.value!!, AssignmentOrigin.VARINIT, pos)
|
||||
decl.value = null
|
||||
return listOf(IAstModification.InsertAfter(
|
||||
|
||||
@@ -98,7 +98,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
// if(rightDt.isBytes)
|
||||
// modifications += IAstModification.ReplaceNode(expr.right, TypecastExpression(expr.right, leftConstAsWord.type, true, expr.right.position), expr)
|
||||
}
|
||||
} else if (parent is TypecastExpression && parent.type == BaseDataType.UWORD && parent.parent is Assignment) {
|
||||
} else if (parent is TypecastExpression && parent.type.isUnsignedWord && parent.parent is Assignment) {
|
||||
val assign = parent.parent as Assignment
|
||||
if (assign.target.inferType(program).isWords) {
|
||||
modifications += IAstModification.ReplaceNode(expr.left, leftConstAsWord, expr)
|
||||
@@ -131,34 +131,34 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
|
||||
if(leftDt issimpletype BaseDataType.BYTE && (rightDt issimpletype BaseDataType.UBYTE || rightDt issimpletype BaseDataType.UWORD)) {
|
||||
// cast left to unsigned
|
||||
val cast = TypecastExpression(expr.left, rightDt.getOrUndef().base, true, expr.left.position)
|
||||
val cast = TypecastExpression(expr.left, rightDt.getOrUndef(), true, expr.left.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
|
||||
}
|
||||
if(leftDt issimpletype BaseDataType.WORD && (rightDt issimpletype BaseDataType.UBYTE || rightDt issimpletype BaseDataType.UWORD)) {
|
||||
// cast left to unsigned word. Cast right to unsigned word if it is ubyte
|
||||
val mods = mutableListOf<IAstModification>()
|
||||
val cast = TypecastExpression(expr.left, BaseDataType.UWORD, true, expr.left.position)
|
||||
val cast = TypecastExpression(expr.left, DataType.UWORD, true, expr.left.position)
|
||||
mods += IAstModification.ReplaceNode(expr.left, cast, expr)
|
||||
if(rightDt issimpletype BaseDataType.UBYTE) {
|
||||
mods += IAstModification.ReplaceNode(expr.right,
|
||||
TypecastExpression(expr.right, BaseDataType.UWORD, true, expr.right.position),
|
||||
TypecastExpression(expr.right, DataType.UWORD, true, expr.right.position),
|
||||
expr)
|
||||
}
|
||||
return mods
|
||||
}
|
||||
if(rightDt issimpletype BaseDataType.BYTE && (leftDt issimpletype BaseDataType.UBYTE || leftDt issimpletype BaseDataType.UWORD)) {
|
||||
// cast right to unsigned
|
||||
val cast = TypecastExpression(expr.right, leftDt.getOrUndef().base, true, expr.right.position)
|
||||
val cast = TypecastExpression(expr.right, leftDt.getOrUndef(), true, expr.right.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, cast, expr))
|
||||
}
|
||||
if(rightDt issimpletype BaseDataType.WORD && (leftDt issimpletype BaseDataType.UBYTE || leftDt issimpletype BaseDataType.UWORD)) {
|
||||
// cast right to unsigned word. Cast left to unsigned word if it is ubyte
|
||||
val mods = mutableListOf<IAstModification>()
|
||||
val cast = TypecastExpression(expr.right, BaseDataType.UWORD, true, expr.right.position)
|
||||
val cast = TypecastExpression(expr.right, DataType.UWORD, true, expr.right.position)
|
||||
mods += IAstModification.ReplaceNode(expr.right, cast, expr)
|
||||
if(leftDt issimpletype BaseDataType.UBYTE) {
|
||||
mods += IAstModification.ReplaceNode(expr.left,
|
||||
TypecastExpression(expr.left, BaseDataType.UWORD, true, expr.left.position),
|
||||
TypecastExpression(expr.left, DataType.UWORD, true, expr.left.position),
|
||||
expr)
|
||||
}
|
||||
return mods
|
||||
@@ -296,7 +296,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
|
||||
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||
// warn about any implicit type casts to Float, because that may not be intended
|
||||
if(typecast.implicit && typecast.type==BaseDataType.FLOAT) {
|
||||
if(typecast.implicit && typecast.type.isFloat) {
|
||||
if(options.floats)
|
||||
errors.info("integer implicitly converted to float. Suggestion: use float literals, add an explicit cast, or revert to integer arithmetic", typecast.position)
|
||||
else
|
||||
@@ -556,7 +556,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
private fun addTypecastOrCastedValueModification(
|
||||
modifications: MutableList<IAstModification>,
|
||||
expressionToCast: Expression,
|
||||
requiredType: BaseDataType,
|
||||
requiredType: BaseDataType, // TODO DataType?
|
||||
parent: Node
|
||||
) {
|
||||
val sourceDt = expressionToCast.inferType(program).getOrUndef()
|
||||
@@ -575,7 +575,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
return
|
||||
}
|
||||
}
|
||||
val cast = TypecastExpression(expressionToCast, requiredType, true, expressionToCast.position)
|
||||
val cast = TypecastExpression(expressionToCast, DataType.forDt(requiredType), true, expressionToCast.position)
|
||||
modifications += IAstModification.ReplaceNode(expressionToCast, cast, parent)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,29 +4,28 @@ import prog8.ast.FatalAstException
|
||||
import prog8.code.ast.PtExpression
|
||||
import prog8.code.ast.PtFunctionCall
|
||||
import prog8.code.ast.PtTypeCast
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.DataType
|
||||
|
||||
|
||||
internal fun makePushPopFunctionCalls(value: PtExpression): Pair<PtFunctionCall, PtExpression> {
|
||||
var popTypecast: BaseDataType? = null
|
||||
var pushTypecast: BaseDataType? = null
|
||||
var popTypecast: DataType? = null
|
||||
var pushTypecast: DataType? = null
|
||||
var pushWord = false
|
||||
var pushFloat = false
|
||||
|
||||
when {
|
||||
value.type.isBool -> {
|
||||
pushTypecast = BaseDataType.UBYTE
|
||||
popTypecast = BaseDataType.BOOL
|
||||
pushTypecast = DataType.UBYTE
|
||||
popTypecast = DataType.BOOL
|
||||
}
|
||||
value.type.isSignedByte -> {
|
||||
pushTypecast = BaseDataType.UBYTE
|
||||
popTypecast = BaseDataType.BYTE
|
||||
pushTypecast = DataType.UBYTE
|
||||
popTypecast = DataType.BYTE
|
||||
}
|
||||
value.type.isSignedWord -> {
|
||||
pushWord = true
|
||||
pushTypecast = BaseDataType.UWORD
|
||||
popTypecast = BaseDataType.WORD
|
||||
pushTypecast = DataType.UWORD
|
||||
popTypecast = DataType.WORD
|
||||
}
|
||||
value.type.isUnsignedByte -> {}
|
||||
value.type.isUnsignedWord -> pushWord = true
|
||||
|
||||
@@ -132,14 +132,17 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
if(constValue!=null)
|
||||
return listOf(IAstModification.ReplaceNode(typecast, constValue, parent))
|
||||
|
||||
if(typecast.expression is NumericLiteral) {
|
||||
val value = (typecast.expression as NumericLiteral).cast(typecast.type, typecast.implicit)
|
||||
if(value.isValid)
|
||||
return listOf(IAstModification.ReplaceNode(typecast, value.valueOrZero(), parent))
|
||||
val number = typecast.expression as? NumericLiteral
|
||||
if(number!=null) {
|
||||
if(typecast.type.isBasic) {
|
||||
val value = number.cast(typecast.type.base, typecast.implicit)
|
||||
if (value.isValid)
|
||||
return listOf(IAstModification.ReplaceNode(typecast, value.valueOrZero(), parent))
|
||||
}
|
||||
}
|
||||
|
||||
val sourceDt = typecast.expression.inferType(program)
|
||||
if(sourceDt issimpletype typecast.type)
|
||||
if(sourceDt istype typecast.type)
|
||||
return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
|
||||
|
||||
if(parent is Assignment) {
|
||||
@@ -423,7 +426,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
if(valueDt.isWords)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, functionCallExpr.args[0], parent))
|
||||
if(valueDt.isBytes) {
|
||||
val cast = TypecastExpression(functionCallExpr.args[0], BaseDataType.UWORD, true, functionCallExpr.position)
|
||||
val cast = TypecastExpression(functionCallExpr.args[0], DataType.UWORD, true, functionCallExpr.position)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, cast, parent))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ other {
|
||||
|
||||
test("generated constvalue from typecast inherits proper parent linkage") {
|
||||
val number = NumericLiteral(BaseDataType.UBYTE, 11.0, Position.DUMMY)
|
||||
val tc = TypecastExpression(number, BaseDataType.BYTE, false, Position.DUMMY)
|
||||
val tc = TypecastExpression(number, DataType.BYTE, false, Position.DUMMY)
|
||||
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||
tc.linkParents(ParentSentinel)
|
||||
tc.parent shouldNotBe null
|
||||
@@ -985,13 +985,13 @@ main {
|
||||
funcarg2 shouldBe NumericLiteral(BaseDataType.UWORD, 8.0, Position.DUMMY)
|
||||
|
||||
val answer3ValueTc = (st[15] as Assignment).value as TypecastExpression
|
||||
answer3ValueTc.type shouldBe BaseDataType.UWORD
|
||||
answer3ValueTc.type shouldBe DataType.UWORD
|
||||
val answer3Value = answer3ValueTc.expression as FunctionCallExpression
|
||||
answer3Value.target.nameInSource shouldBe listOf("msb")
|
||||
answer3Value.args.single() shouldBe instanceOf<BinaryExpression>()
|
||||
|
||||
val funcarg3tc = (st[16] as FunctionCallStatement).args.single() as TypecastExpression
|
||||
funcarg3tc.type shouldBe BaseDataType.UWORD
|
||||
funcarg3tc.type shouldBe DataType.UWORD
|
||||
val funcarg3 = funcarg3tc.expression as FunctionCallExpression
|
||||
funcarg3.target.nameInSource shouldBe listOf("msb")
|
||||
funcarg3.args.single() shouldBe instanceOf<BinaryExpression>()
|
||||
|
||||
@@ -227,11 +227,11 @@ main {
|
||||
stmts.size shouldBe 4
|
||||
val assign1tc = (stmts[2] as Assignment).value as TypecastExpression
|
||||
val assign2tc = (stmts[3] as Assignment).value as TypecastExpression
|
||||
assign1tc.type shouldBe BaseDataType.WORD
|
||||
assign2tc.type shouldBe BaseDataType.WORD
|
||||
assign1tc.type shouldBe DataType.WORD
|
||||
assign2tc.type shouldBe DataType.WORD
|
||||
assign2tc.expression shouldBe instanceOf<IdentifierReference>()
|
||||
val assign1subtc = (assign1tc.expression as TypecastExpression)
|
||||
assign1subtc.type shouldBe BaseDataType.BYTE
|
||||
assign1subtc.type shouldBe DataType.BYTE
|
||||
assign1subtc.expression shouldBe instanceOf<IdentifierReference>()
|
||||
}
|
||||
|
||||
@@ -942,7 +942,7 @@ main {
|
||||
(v2.right as NumericLiteral).number shouldBe 399
|
||||
|
||||
val v3 = (st[4] as Assignment).value as TypecastExpression
|
||||
v3.type shouldBe BaseDataType.UWORD
|
||||
v3.type shouldBe DataType.UWORD
|
||||
val v3e = v3.expression as BinaryExpression
|
||||
v3e.operator shouldBe "*"
|
||||
(v3e.left as IdentifierReference).nameInSource shouldBe listOf("cx16","r0L")
|
||||
@@ -952,7 +952,7 @@ main {
|
||||
val v4 = (st[5] as Assignment).value as BinaryExpression
|
||||
v4.operator shouldBe "*"
|
||||
val v4t = v4.left as TypecastExpression
|
||||
v4t.type shouldBe BaseDataType.UWORD
|
||||
v4t.type shouldBe DataType.UWORD
|
||||
(v4t.expression as IdentifierReference).nameInSource shouldBe listOf("cx16","r0L")
|
||||
(v4.right as NumericLiteral).type shouldBe BaseDataType.UWORD
|
||||
(v4.right as NumericLiteral).number shouldBe 5
|
||||
|
||||
@@ -840,12 +840,12 @@ main {
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 8
|
||||
val assignUbbVal = ((st[5] as Assignment).value as TypecastExpression)
|
||||
assignUbbVal.type shouldBe BaseDataType.UBYTE
|
||||
assignUbbVal.type shouldBe DataType.UBYTE
|
||||
assignUbbVal.expression shouldBe instanceOf<IdentifierReference>()
|
||||
val assignVaddr = (st[7] as Assignment).value as FunctionCallExpression
|
||||
assignVaddr.target.nameInSource shouldBe listOf("mkword")
|
||||
val tc = assignVaddr.args[0] as TypecastExpression
|
||||
tc.type shouldBe BaseDataType.UBYTE
|
||||
tc.type shouldBe DataType.UBYTE
|
||||
tc.expression shouldBe instanceOf<ArrayIndexedExpression>()
|
||||
}
|
||||
|
||||
|
||||
@@ -114,7 +114,6 @@ class TestAsmGenSymbols: StringSpec({
|
||||
val localvarIdentScoped = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.start.localvar" }.value as PtAddressOf).identifier
|
||||
asmgen.asmSymbolName(localvarIdentScoped) shouldBe "localvar"
|
||||
asmgen.asmVariableName(localvarIdentScoped) shouldBe "localvar"
|
||||
|
||||
val scopedVarIdent = (sub.children.asSequence().filterIsInstance<PtAssignment>().first { (it.value as? PtAddressOf)?.identifier?.name=="main.var_outside" }.value as PtAddressOf).identifier
|
||||
asmgen.asmSymbolName(scopedVarIdent) shouldBe "main.var_outside"
|
||||
asmgen.asmVariableName(scopedVarIdent) shouldBe "main.var_outside"
|
||||
|
||||
@@ -21,7 +21,9 @@ internal object DummyFunctions : IBuiltinFunctions {
|
||||
|
||||
internal object DummyMemsizer : IMemSizer {
|
||||
override fun memorySize(dt: DataType, numElements: Int?): Int {
|
||||
if(dt.isArray || dt.isSplitWordArray) {
|
||||
if(dt.isPointerArray)
|
||||
return 2 * numElements!!
|
||||
else if(dt.isArray || dt.isSplitWordArray) {
|
||||
require(numElements!=null)
|
||||
return when(dt.sub) {
|
||||
BaseDataType.BOOL, BaseDataType.BYTE, BaseDataType.UBYTE -> numElements
|
||||
|
||||
@@ -155,6 +155,14 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(struct: StructDecl) {
|
||||
outputln("struct ${struct.name} {")
|
||||
for(member in struct.members) {
|
||||
outputlni( " ${member.first} ${member.second}")
|
||||
}
|
||||
outputlni("}")
|
||||
}
|
||||
|
||||
override fun visit(subroutine: Subroutine) {
|
||||
output("\n")
|
||||
outputi("")
|
||||
@@ -454,7 +462,7 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
|
||||
override fun visit(typecast: TypecastExpression) {
|
||||
output("(")
|
||||
typecast.expression.accept(this)
|
||||
output(" as ${DataType.forDt(typecast.type).sourceString()}) ")
|
||||
output(" as ${typecast.type} ")
|
||||
}
|
||||
|
||||
override fun visit(memread: DirectMemoryRead) {
|
||||
@@ -474,7 +482,7 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
|
||||
if(addressOf.msb)
|
||||
output(">")
|
||||
addressOf.identifier.accept(this)
|
||||
if(addressOf.arrayIndex!=null) {
|
||||
if (addressOf.arrayIndex != null) {
|
||||
output("[")
|
||||
addressOf.arrayIndex?.accept(this)
|
||||
output("]")
|
||||
|
||||
@@ -45,6 +45,7 @@ internal fun BlockContext.toAst(isInLibrary: Boolean) : Block {
|
||||
it.inlineir()!=null -> it.inlineir().toAst()
|
||||
it.labeldef()!=null -> it.labeldef().toAst()
|
||||
it.alias()!=null -> it.alias().toAst()
|
||||
it.structdeclaration()!=null -> it.structdeclaration().toAst()
|
||||
else -> throw FatalAstException("weird block node $it")
|
||||
}
|
||||
}
|
||||
@@ -54,7 +55,7 @@ internal fun BlockContext.toAst(isInLibrary: Boolean) : Block {
|
||||
private fun Statement_blockContext.toAst(): MutableList<Statement> =
|
||||
statement().asSequence().map { it.toAst() }.toMutableList()
|
||||
|
||||
private fun VariabledeclarationContext.toAst() : Statement {
|
||||
private fun VariabledeclarationContext.toAst() : VarDecl {
|
||||
vardecl()?.let {
|
||||
return it.toAst(VarDeclType.VAR, null)
|
||||
}
|
||||
@@ -76,6 +77,12 @@ private fun VariabledeclarationContext.toAst() : Statement {
|
||||
throw FatalAstException("weird variable decl $this")
|
||||
}
|
||||
|
||||
private fun StructdeclarationContext.toAst(): Statement {
|
||||
val name = identifier().text
|
||||
val members = structfielddecl().map { it.toAst() }
|
||||
return StructDecl(name, members, toPosition())
|
||||
}
|
||||
|
||||
private fun SubroutinedeclarationContext.toAst() : Subroutine {
|
||||
return when {
|
||||
subroutine()!=null -> subroutine().toAst()
|
||||
@@ -165,6 +172,9 @@ private fun StatementContext.toAst() : Statement {
|
||||
val aliasstmt = alias()?.toAst()
|
||||
if(aliasstmt!=null) return aliasstmt
|
||||
|
||||
val structdecl = structdeclaration()?.toAst()
|
||||
if(structdecl!=null) return structdecl
|
||||
|
||||
throw FatalAstException("unprocessed source text (are we missing ast conversion rules for parser elements?): $text")
|
||||
}
|
||||
|
||||
@@ -231,18 +241,12 @@ private fun Asmsub_returnsContext.toAst(): List<AsmSubroutineReturn>
|
||||
else -> throw SyntaxError("invalid register or status flag", toPosition())
|
||||
}
|
||||
}
|
||||
// asmsubs currently only return a base datatype
|
||||
val returnBaseDt = it.datatype().toAst()
|
||||
AsmSubroutineReturn(
|
||||
DataType.forDt(returnBaseDt),
|
||||
registerorpair,
|
||||
statusregister)
|
||||
AsmSubroutineReturn(it.datatype().toAst(), registerorpair, statusregister)
|
||||
}
|
||||
|
||||
private fun Asmsub_paramsContext.toAst(): List<AsmSubroutineParameter> = asmsub_param().map {
|
||||
val vardecl = it.vardecl()
|
||||
val baseDt = vardecl.datatype()?.toAst() ?: BaseDataType.UNDEFINED
|
||||
var datatype = DataType.forDt(baseDt)
|
||||
var datatype = vardecl.datatype()?.toAst() ?: DataType.UNDEFINED
|
||||
if(vardecl.ARRAYSIG()!=null || vardecl.arrayindex()!=null)
|
||||
datatype = datatype.elementToArray()
|
||||
val (registerorpair, statusregister) = parseParamRegister(it.register, it.toPosition())
|
||||
@@ -319,7 +323,7 @@ private fun SubroutineContext.toAst() : Subroutine {
|
||||
return Subroutine(
|
||||
identifier().text,
|
||||
sub_params()?.toAst()?.toMutableList() ?: mutableListOf(),
|
||||
returntypes.map { DataType.forDt(it) }.toMutableList(),
|
||||
returntypes.toMutableList(),
|
||||
emptyList(),
|
||||
emptyList(),
|
||||
emptySet(),
|
||||
@@ -341,8 +345,7 @@ private fun Sub_paramsContext.toAst(): List<SubroutineParameter> =
|
||||
throw SyntaxError("invalid parameter tag '$tag'", toPosition())
|
||||
}
|
||||
val zp = getZpOption(tags)
|
||||
val baseDt = decl.datatype()?.toAst() ?: BaseDataType.UNDEFINED
|
||||
var datatype = DataType.forDt(baseDt)
|
||||
var datatype = decl.datatype()?.toAst() ?: DataType.UNDEFINED
|
||||
if(decl.ARRAYSIG()!=null || decl.arrayindex()!=null)
|
||||
datatype = datatype.elementToArray()
|
||||
|
||||
@@ -380,16 +383,16 @@ private fun Assign_targetContext.toAst() : AssignTarget {
|
||||
AssignTarget(identifier, null, null, null, false, scoped_identifier().toPosition())
|
||||
}
|
||||
is MemoryTargetContext ->
|
||||
AssignTarget(null, null, DirectMemoryWrite(directmemory().expression().toAst(), directmemory().toPosition()), null, false, toPosition())
|
||||
AssignTarget(null, null, DirectMemoryWrite(directmemory().expression().toAst(), directmemory().toPosition()),null,false, toPosition())
|
||||
is ArrayindexedTargetContext -> {
|
||||
val ax = arrayindexed()
|
||||
val arrayvar = ax.scoped_identifier().toAst()
|
||||
val index = ax.arrayindex().toAst()
|
||||
val arrayindexed = ArrayIndexedExpression(arrayvar, index, ax.toPosition())
|
||||
AssignTarget(null, arrayindexed, null, null, false, toPosition())
|
||||
AssignTarget(null, arrayindexed, null,null,false, toPosition())
|
||||
}
|
||||
is VoidTargetContext -> {
|
||||
AssignTarget(null, null, null, null, true, void_().toPosition())
|
||||
AssignTarget(null, null, null,null,true, void_().toPosition())
|
||||
}
|
||||
else -> throw FatalAstException("weird assign target node $this")
|
||||
}
|
||||
@@ -397,7 +400,7 @@ private fun Assign_targetContext.toAst() : AssignTarget {
|
||||
|
||||
private fun Multi_assign_targetContext.toAst() : AssignTarget {
|
||||
val targets = this.assign_target().map { it.toAst() }
|
||||
return AssignTarget(null, null, null, targets, false, toPosition())
|
||||
return AssignTarget(null, null, null,targets, false, toPosition())
|
||||
}
|
||||
|
||||
private fun ClobberContext.toAst() : Set<CpuRegister> {
|
||||
@@ -430,7 +433,7 @@ private fun AugassignmentContext.toAst(): Assignment {
|
||||
return Assignment(assign_target().toAst(), expression, AssignmentOrigin.USERCODE, toPosition())
|
||||
}
|
||||
|
||||
private fun DatatypeContext.toAst(): BaseDataType {
|
||||
private fun BasedatatypeContext.toAst(): BaseDataType {
|
||||
return try {
|
||||
BaseDataType.valueOf(text.uppercase())
|
||||
} catch (_: IllegalArgumentException) {
|
||||
@@ -438,6 +441,22 @@ private fun DatatypeContext.toAst(): BaseDataType {
|
||||
}
|
||||
}
|
||||
|
||||
private fun DatatypeContext.toAst(): DataType {
|
||||
val base = basedatatype()?.toAst()
|
||||
if(base!=null)
|
||||
return DataType.forDt(base)
|
||||
val pointer = pointertype().toAst()
|
||||
return pointer
|
||||
}
|
||||
|
||||
private fun PointertypeContext.toAst(): DataType {
|
||||
val base = basedatatype()?.toAst()
|
||||
if(base!=null)
|
||||
return DataType.pointer(base)
|
||||
val identifier = scoped_identifier().identifier().map { it.text}
|
||||
return DataType.pointer(identifier)
|
||||
}
|
||||
|
||||
private fun ArrayindexContext.toAst() : ArrayIndex =
|
||||
ArrayIndex(expression().toAst(), toPosition())
|
||||
|
||||
@@ -589,9 +608,8 @@ private fun ExpressionContext.toAst(insideParentheses: Boolean=false) : Expressi
|
||||
return expression(0).toAst(insideParentheses=true) // expression within ( )
|
||||
|
||||
if(typecast()!=null) {
|
||||
// typecast is always to a base datatype
|
||||
val baseDt = typecast().datatype().toAst()
|
||||
return TypecastExpression(expression(0).toAst(), baseDt, false, toPosition())
|
||||
val dt = typecast().datatype().toAst()
|
||||
return TypecastExpression(expression(0).toAst(), dt, false, toPosition())
|
||||
}
|
||||
|
||||
if(directmemory()!=null)
|
||||
@@ -603,7 +621,7 @@ private fun ExpressionContext.toAst(insideParentheses: Boolean=false) : Expressi
|
||||
val msb = addressOf.ADDRESS_OF_MSB()!=null
|
||||
// note: &< (ADDRESS_OF_LSB) is equivalent to a regular &.
|
||||
return if (identifier != null)
|
||||
AddressOf(addressof().scoped_identifier().toAst(), null, msb, toPosition())
|
||||
AddressOf(addressof().scoped_identifier().toAst(),null, msb, toPosition())
|
||||
else {
|
||||
val array = addressOf.arrayindexed()
|
||||
AddressOf(array.scoped_identifier().toAst(), array.arrayindex().toAst(), msb, toPosition())
|
||||
@@ -772,6 +790,12 @@ private fun When_choiceContext.toAst(): WhenChoice {
|
||||
return WhenChoice(values?.toMutableList(), scope, toPosition())
|
||||
}
|
||||
|
||||
private fun StructfielddeclContext.toAst(): Pair<DataType, String> {
|
||||
val identifier = identifier().NAME().text
|
||||
val dt = datatype().toAst()
|
||||
return dt to identifier
|
||||
}
|
||||
|
||||
private fun VardeclContext.toAst(type: VarDeclType, value: Expression?): VarDecl {
|
||||
val tags = TAG().map { it.text }
|
||||
val validTags = arrayOf("@zp", "@requirezp", "@nozp", "@split", "@nosplit", "@shared", "@alignword", "@alignpage", "@align64", "@dirty")
|
||||
@@ -790,8 +814,13 @@ private fun VardeclContext.toAst(type: VarDeclType, value: Expression?): VarDecl
|
||||
val alignpage = "@alignpage" in tags
|
||||
if(alignpage && alignword)
|
||||
throw SyntaxError("choose a single alignment option", toPosition())
|
||||
val baseDt = datatype()?.toAst() ?: BaseDataType.UNDEFINED
|
||||
val dt = if(isArray) DataType.arrayFor(baseDt, split!=SplitWish.NOSPLIT) else DataType.forDt(baseDt)
|
||||
val baseDt = datatype()?.toAst() ?: DataType.UNDEFINED
|
||||
val dt = if(!isArray) baseDt else {
|
||||
if(baseDt.isPointer)
|
||||
DataType.arrayOfPointersTo(baseDt.sub, baseDt.subIdentifier)
|
||||
else
|
||||
DataType.arrayFor(baseDt.base, split!=SplitWish.NOSPLIT)
|
||||
}
|
||||
|
||||
return VarDecl(
|
||||
type, VarDeclOrigin.USERCODE,
|
||||
|
||||
@@ -70,10 +70,10 @@ sealed class Expression: Node {
|
||||
if(sourceDt.base==targetDt && sourceDt.sub==null)
|
||||
return Pair(false, this)
|
||||
if(this is TypecastExpression) {
|
||||
this.type = targetDt
|
||||
this.type = DataType.forDt(targetDt)
|
||||
return Pair(false, this)
|
||||
}
|
||||
val typecast = TypecastExpression(this, targetDt, implicit, this.position)
|
||||
val typecast = TypecastExpression(this, DataType.forDt(targetDt), implicit, this.position)
|
||||
return Pair(true, typecast)
|
||||
}
|
||||
}
|
||||
@@ -369,7 +369,7 @@ class ArrayIndexedExpression(var arrayvar: IdentifierReference,
|
||||
override fun copy() = ArrayIndexedExpression(arrayvar.copy(), indexer.copy(), position)
|
||||
}
|
||||
|
||||
class TypecastExpression(var expression: Expression, var type: BaseDataType, val implicit: Boolean, override val position: Position) : Expression() {
|
||||
class TypecastExpression(var expression: Expression, var type: DataType, val implicit: Boolean, override val position: Position) : Expression() {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
@@ -378,7 +378,7 @@ class TypecastExpression(var expression: Expression, var type: BaseDataType, val
|
||||
}
|
||||
|
||||
init {
|
||||
if(type==BaseDataType.BOOL) require(!implicit) {"no implicit cast to boolean allowed"}
|
||||
if(type.isBool) require(!implicit) {"no implicit cast to boolean allowed"}
|
||||
}
|
||||
|
||||
override val isSimple = expression.isSimple
|
||||
@@ -396,9 +396,11 @@ class TypecastExpression(var expression: Expression, var type: BaseDataType, val
|
||||
override fun referencesIdentifier(nameInSource: List<String>) = expression.referencesIdentifier(nameInSource)
|
||||
override fun inferType(program: Program) = InferredTypes.knownFor(type)
|
||||
override fun constValue(program: Program): NumericLiteral? {
|
||||
if(!type.isBasic)
|
||||
return null
|
||||
val cv = expression.constValue(program) ?: return null
|
||||
cv.linkParents(parent)
|
||||
val cast = cv.cast(type, implicit)
|
||||
val cast = cv.cast(type.base, implicit)
|
||||
return if(cast.isValid) {
|
||||
val newval = cast.valueOrZero()
|
||||
newval.linkParents(parent)
|
||||
@@ -425,17 +427,21 @@ data class AddressOf(var identifier: IdentifierReference, var arrayIndex: ArrayI
|
||||
override val isSimple = true
|
||||
|
||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||
if(node===identifier) {
|
||||
require(replacement is IdentifierReference)
|
||||
identifier = replacement
|
||||
replacement.parent = this
|
||||
} else if(node===arrayIndex) {
|
||||
require(replacement is ArrayIndex)
|
||||
arrayIndex = replacement
|
||||
replacement.parent = this
|
||||
} else {
|
||||
throw FatalAstException("invalid replace, no child node $node")
|
||||
when {
|
||||
node===identifier -> {
|
||||
require(replacement is IdentifierReference)
|
||||
identifier = replacement
|
||||
arrayIndex = null
|
||||
}
|
||||
node===arrayIndex -> {
|
||||
require(replacement is ArrayIndex)
|
||||
arrayIndex = replacement
|
||||
}
|
||||
else -> {
|
||||
throw FatalAstException("invalid replace, no child node $node")
|
||||
}
|
||||
}
|
||||
replacement.parent = this
|
||||
}
|
||||
|
||||
override fun copy() = AddressOf(identifier.copy(), arrayIndex?.copy(), msb, position)
|
||||
@@ -1164,7 +1170,7 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
||||
|
||||
override val isSimple = true
|
||||
|
||||
fun targetStatement(program: Program?) =
|
||||
fun targetStatement(program: Program?): Statement? =
|
||||
if(program!=null && nameInSource.singleOrNull() in program.builtinFunctions.names)
|
||||
BuiltinFunctionPlaceholder(nameInSource[0], position, parent)
|
||||
else
|
||||
@@ -1172,6 +1178,7 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
||||
|
||||
fun targetVarDecl(): VarDecl? = targetStatement(null) as? VarDecl
|
||||
fun targetSubroutine(): Subroutine? = targetStatement(null) as? Subroutine
|
||||
fun targetStructDecl(): StructDecl? = targetStatement(null) as? StructDecl
|
||||
|
||||
fun targetNameAndType(program: Program): Pair<String, DataType> {
|
||||
val target = targetStatement(program) as? INamedStatement ?: throw FatalAstException("can't find target for $nameInSource")
|
||||
@@ -1251,6 +1258,7 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class FunctionCallExpression(override var target: IdentifierReference,
|
||||
override val args: MutableList<Expression>,
|
||||
override val position: Position) : Expression(), IFunctionCall {
|
||||
@@ -1437,7 +1445,7 @@ class IfExpression(var condition: Expression, var truevalue: Expression, var fal
|
||||
|
||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||
if(replacement !is Expression)
|
||||
throw throw FatalAstException("invalid replace")
|
||||
throw FatalAstException("invalid replace")
|
||||
if(node===condition) condition=replacement
|
||||
else if(node===truevalue) truevalue=replacement
|
||||
else if(node===falsevalue) falsevalue=replacement
|
||||
|
||||
@@ -21,7 +21,7 @@ object InferredTypes {
|
||||
false
|
||||
else if(type==BaseDataType.STR && this.datatype?.base==BaseDataType.STR)
|
||||
true
|
||||
else (this.datatype?.base == type && this.datatype.sub == null) // strict equality if known
|
||||
else (this.datatype?.base == type && this.datatype.isBasic) // strict equality if known
|
||||
|
||||
companion object {
|
||||
fun unknown() = InferredType(isUnknown = true, isVoid = false, datatype = null)
|
||||
@@ -82,6 +82,13 @@ object InferredTypes {
|
||||
type.isFloat -> InferredType.known(BaseDataType.FLOAT)
|
||||
type.isString -> InferredType.known(BaseDataType.STR)
|
||||
type.isLong -> InferredType.known(BaseDataType.LONG)
|
||||
type.isPointerArray -> InferredType.known(DataType.arrayOfPointersTo(type.sub, type.subIdentifier))
|
||||
type.isPointer -> {
|
||||
if(type.sub!=null)
|
||||
InferredType.known(DataType.pointer(type.sub!!))
|
||||
else
|
||||
InferredType.known(DataType.pointer(type.subIdentifier!!))
|
||||
}
|
||||
type.isSplitWordArray -> {
|
||||
when(type.sub) {
|
||||
BaseDataType.UWORD -> InferredType.known(DataType.arrayFor(BaseDataType.UWORD))
|
||||
|
||||
@@ -374,6 +374,42 @@ class VarDecl(val type: VarDeclType,
|
||||
}
|
||||
}
|
||||
|
||||
class StructDecl(override val name: String, val members: List<Pair<DataType, String>>, override val position: Position) : Statement(), INamedStatement {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
}
|
||||
|
||||
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||
override fun referencesIdentifier(nameInSource: List<String>) = false
|
||||
override fun copy() = StructDecl(name, members.toList(), position)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||
fun memsize(sizer: IMemSizer): Int = members.sumOf { sizer.memorySize(it.first, 1) }
|
||||
fun getField(name: String): Pair<DataType, String>? = members.firstOrNull { it.second==name }
|
||||
}
|
||||
|
||||
class StructFieldRef(val pointer: IdentifierReference, val struct: StructDecl, val type: DataType, override val name: String, override val position: Position): Statement(), INamedStatement {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
// pointer and struct are not our property!
|
||||
}
|
||||
|
||||
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||
|
||||
override fun referencesIdentifier(nameInSource: List<String>) = pointer.referencesIdentifier(nameInSource) || struct.referencesIdentifier(nameInSource)
|
||||
|
||||
override fun copy(): StructFieldRef = StructFieldRef(pointer.copy(), struct.copy(), type, name, position)
|
||||
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||
|
||||
}
|
||||
|
||||
|
||||
class ArrayIndex(var indexExpr: Expression,
|
||||
override val position: Position) : Node {
|
||||
override lateinit var parent: Node
|
||||
@@ -545,8 +581,15 @@ data class AssignTarget(var identifier: IdentifierReference?,
|
||||
|
||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||
when {
|
||||
node === identifier -> identifier = replacement as IdentifierReference
|
||||
node === arrayindexed -> arrayindexed = replacement as ArrayIndexedExpression
|
||||
node === identifier -> {
|
||||
require(replacement is IdentifierReference)
|
||||
identifier = replacement
|
||||
arrayindexed = null
|
||||
}
|
||||
node === arrayindexed -> {
|
||||
arrayindexed = replacement as ArrayIndexedExpression
|
||||
identifier = null
|
||||
}
|
||||
node === multi -> throw FatalAstException("can't replace multi assign targets")
|
||||
else -> throw FatalAstException("invalid replace")
|
||||
}
|
||||
|
||||
@@ -105,6 +105,8 @@ abstract class AstWalker {
|
||||
open fun before(continueStmt: Continue, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun before(containment: ContainmentCheck, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun before(decl: VarDecl, 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
|
||||
open fun before(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun before(expr: PrefixExpression, parent: Node): Iterable<IAstModification> = noModifications
|
||||
@@ -150,6 +152,8 @@ abstract class AstWalker {
|
||||
open fun after(continueStmt: Continue, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun after(containment: ContainmentCheck, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun after(decl: VarDecl, 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
|
||||
open fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> = noModifications
|
||||
@@ -269,6 +273,16 @@ abstract class AstWalker {
|
||||
track(after(decl, parent), decl, parent)
|
||||
}
|
||||
|
||||
fun visit(struct: StructDecl, parent: Node) {
|
||||
track(before(struct, parent), struct, parent)
|
||||
track(after(struct, parent), struct, parent)
|
||||
}
|
||||
|
||||
fun visit(field: StructFieldRef, parent: Node) {
|
||||
track(before(field, parent), field, parent)
|
||||
track(after(field, parent), field, parent)
|
||||
}
|
||||
|
||||
fun visit(subroutine: Subroutine, parent: Node) {
|
||||
track(before(subroutine, parent), subroutine, parent)
|
||||
subroutine.asmAddress?.varbank?.accept(this, subroutine)
|
||||
|
||||
@@ -44,6 +44,12 @@ interface IAstVisitor {
|
||||
decl.arraysize?.accept(this)
|
||||
}
|
||||
|
||||
fun visit(struct: StructDecl) {
|
||||
}
|
||||
|
||||
fun visit(field: StructFieldRef) {
|
||||
}
|
||||
|
||||
fun visit(subroutine: Subroutine) {
|
||||
subroutine.asmAddress?.varbank?.accept(this)
|
||||
subroutine.statements.forEach { it.accept(this) }
|
||||
|
||||
+25
-2
@@ -1,14 +1,37 @@
|
||||
TODO
|
||||
====
|
||||
|
||||
STRUCTS and TYPED POINTERS
|
||||
--------------------------
|
||||
|
||||
...
|
||||
- add ast check for assignments to struct fields type checks node_ptr.nextnode = enemy_ptr
|
||||
- add IR LOADPIX/STOREPIX instructions for efficient field access through a pointer var
|
||||
- DONE: declare struct as a separate entity so you can then declare multiple variables (pointers) of the same struct type. Like usual.
|
||||
- struct is a 'packed' struct, fields are placed in order of declaration. This guarantees exact size and place of the fields
|
||||
- structs only supported as a reference type (uword pointer). This removes a lot of the problems related to introducing a variable length value type.
|
||||
- need to introduce typed pointer datatype in prog8 to allow this to make any sense. + correct code gen
|
||||
- initially only a pointer-to-struct should actually work, pointer-to-other-type is possible but that can come later.
|
||||
- DONE: a struct can contain only numeric type fields (byte,word,float) - no nested structs, no reference types (strings, arrays) inside structs.
|
||||
- DONE: struct might also contain typed pointer fields (because a pointer is just an address word)
|
||||
- max 1 page of memory total size to allow regular register indexing
|
||||
- DONE: assigning ptrs of different types is only allowed via a cast as usual. For simple address (uword) assignments, no cast is needed (but allowed)
|
||||
- how to dereference a pointer? Pascal does it like this: ptr^
|
||||
- dereferencing a pointer to struct could look like Pascal's ptr^.field as well, but the ^ is actually redundant here; compiler already knows it's a pointer type.
|
||||
Note that actually dereferencing a pointer to a struct as an explicit operation, conflicts with the third axiom on this list (structs only as reference types) so it can only be done for basic types?
|
||||
So... setting struct fields can simply be ``structvar.field = 42`` and reading them ``a = structvar.field``
|
||||
- DONE: you should be able to get the address of an individual field: ``&structpointer.field``
|
||||
- arrays of structs? Just an array of uword pointers to said structs. Can even be @split as the only representation form because that's the default for word arrays.
|
||||
- DONE: need to teach sizeof() how to calculate struct sizes (need unit test + doc)
|
||||
- static initialization of structs may be allowed only at block scope and then behaves like arrays; it won't reset to the original value when program is restarted, so beware. Syntax = TBD
|
||||
- allow memory-mapped structs? Something like &Sprite sprite0 = $9000 basically behaves identically to a typed pointer, but the address is immutable as usual
|
||||
- existing STR and ARRAY remain unchanged (don't become typed pointers) so we can keep doing register-indexed addressing directly on them
|
||||
- rather than str or uword parameter types for routines with a string argument, use ^str (or ^ubyte maybe? these are more or less identical..?)
|
||||
- same for arrays? pointer-to-array syntax = TBD
|
||||
|
||||
|
||||
Future Things and Ideas
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- STRUCTS: are now being developed in their own separate branch "structs". This will be for the next major version of the compiler (v12)
|
||||
- is "checkAssignmentCompatible" redundant (gets called just 1 time!) when we also have "checkValueTypeAndRange" ?
|
||||
- romable: should we have a way to explicitly set the memory address for the BSS area (instead of only the highram bank number on X16, allow a memory address too for the -varshigh option?)
|
||||
- Kotlin: can we use inline value classes in certain spots?
|
||||
|
||||
+71
-7
@@ -1,15 +1,79 @@
|
||||
%zeropage basicsafe
|
||||
%import textio
|
||||
%import strings
|
||||
%import floats
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
str name1 = sc:"irmen de jong 123456789 the quick brown fox"
|
||||
str name2 = sc:"jumps over the lazy frog"
|
||||
|
||||
txt.print_ub(strings.hash(name1))
|
||||
struct Enemy {
|
||||
ubyte x
|
||||
ubyte y
|
||||
uword value
|
||||
float rotation
|
||||
bool alive
|
||||
; no strings or arrays allowed in struct type declarations.
|
||||
; typed pointers are allowed though because these are just a uword:
|
||||
^float floatptr
|
||||
^str stringpointer
|
||||
}
|
||||
|
||||
sub start() {
|
||||
|
||||
; struct declarations also allowed inside subroutine scope
|
||||
struct Node {
|
||||
ubyte type
|
||||
uword value
|
||||
^Node nextnode ; linked list?
|
||||
}
|
||||
|
||||
; declare pointer vars
|
||||
^bool @shared bool_ptr
|
||||
^ubyte @shared ubyte_ptr
|
||||
^word @shared word_ptr
|
||||
^Node @shared node_ptr
|
||||
^Enemy @shared enemy_ptr
|
||||
^bool[5] @shared boolptr_list ; array of pointers to bools (bit silly, should we even support this)
|
||||
^Node[5] @shared node_list ; array of pointers to nodes
|
||||
^Enemy[5] @shared enemy_list ; array of pointers to enemies
|
||||
|
||||
txt.print("sizeofs: ")
|
||||
txt.print_ub(sizeof(Enemy))
|
||||
txt.spc()
|
||||
txt.print_ub(strings.hash(name2))
|
||||
txt.print_ub(sizeof(Node))
|
||||
txt.spc()
|
||||
txt.print_ub(sizeof(bool_ptr))
|
||||
txt.nl()
|
||||
|
||||
; point to a memory address.
|
||||
bool_ptr = 2000
|
||||
bool_ptr = 2002 as ^bool
|
||||
ubyte_ptr = 2000
|
||||
word_ptr = 2000
|
||||
node_ptr = 2000
|
||||
enemy_ptr = 2000
|
||||
bool_ptr = enemy_ptr as ^bool ; cast makes no sense, but hey, programmer knows best right? (without cast would give error)
|
||||
; array elements, point to a memory address
|
||||
node_list[0] = 1000
|
||||
node_list[1] = 2000
|
||||
node_list[1] = 2002 as ^Node
|
||||
node_list[2] = 3000
|
||||
|
||||
; BELOW DOESN'T WORK YET:
|
||||
; writing and reading fields
|
||||
enemy_ptr.x = 42
|
||||
enemy_ptr.alive = true
|
||||
node_ptr.nextnode = 2000
|
||||
node_ptr.nextnode = enemy_ptr ; TODO should give type error!
|
||||
node_ptr.nextnode = node_ptr ; link to self
|
||||
node_ptr.nextnode.value = 888 ; traverse multiple pointers
|
||||
main.start.enemy_ptr.value = 600 ; struct ptr vars can occur anywhere in a scoped name, not just the first segment
|
||||
cx16.r0 = enemy_ptr.value
|
||||
|
||||
; address of fields
|
||||
txt.print_uw(&enemy_ptr.alive)
|
||||
txt.nl()
|
||||
|
||||
|
||||
; TODO how to statically allocate/initialize a struct? Difficult.. see TODO in docs
|
||||
}
|
||||
}
|
||||
|
||||
@@ -544,6 +544,18 @@ class IRFileReader {
|
||||
|
||||
private fun parseDatatype(type: String, isArray: Boolean): DataType {
|
||||
if(isArray) {
|
||||
if(type[0]=='^') {
|
||||
return when(type.drop(1)) {
|
||||
"bool" -> DataType.arrayOfPointersTo(BaseDataType.BOOL, null)
|
||||
"byte" -> DataType.arrayOfPointersTo(BaseDataType.BYTE, null)
|
||||
"ubyte", "str" -> DataType.arrayOfPointersTo(BaseDataType.UBYTE, null)
|
||||
"word" -> DataType.arrayOfPointersTo(BaseDataType.WORD, null)
|
||||
"uword" -> DataType.arrayOfPointersTo(BaseDataType.UWORD, null)
|
||||
"float" -> DataType.arrayOfPointersTo(BaseDataType.FLOAT, null)
|
||||
"long" -> DataType.arrayOfPointersTo(BaseDataType.LONG, null)
|
||||
else -> DataType.arrayOfPointersTo(null, type.drop(1).split('.'))
|
||||
}
|
||||
}
|
||||
return when(type) {
|
||||
"bool" -> DataType.arrayFor(BaseDataType.BOOL, false)
|
||||
"byte" -> DataType.arrayFor(BaseDataType.BYTE, false)
|
||||
@@ -555,6 +567,20 @@ class IRFileReader {
|
||||
else -> throw IRParseException("invalid dt $type")
|
||||
}
|
||||
} else {
|
||||
if(type[0]=='^') {
|
||||
// pointer type to either a base datatype, or a struct name
|
||||
return when(type.drop(1)) {
|
||||
"bool" -> DataType.pointer(BaseDataType.BOOL)
|
||||
"byte" -> DataType.pointer(BaseDataType.BYTE)
|
||||
"ubyte" -> DataType.pointer(BaseDataType.UBYTE)
|
||||
"word" -> DataType.pointer(BaseDataType.WORD)
|
||||
"uword" -> DataType.pointer(BaseDataType.UWORD)
|
||||
"float" -> DataType.pointer(BaseDataType.FLOAT)
|
||||
"long" -> DataType.pointer(BaseDataType.LONG)
|
||||
// note: 'str' should not occur anymore in IR. Should be 'uword'
|
||||
else -> DataType.pointer(type.drop(1).split('.'))
|
||||
}
|
||||
}
|
||||
return when(type) {
|
||||
"bool" -> DataType.BOOL
|
||||
"byte" -> DataType.BYTE
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package prog8.intermediate
|
||||
|
||||
import prog8.code.core.*
|
||||
import prog8.Either
|
||||
import prog8.code.core.*
|
||||
import prog8.left
|
||||
import prog8.right
|
||||
|
||||
@@ -17,6 +17,8 @@ fun DataType.irTypeString(length: Int?): String {
|
||||
BaseDataType.LONG -> "long"
|
||||
BaseDataType.FLOAT -> "float"
|
||||
BaseDataType.STR -> "ubyte[$lengthStr]" // here string doesn't exist as a seperate datatype anymore
|
||||
BaseDataType.POINTER -> if(sub!=null) "^${sub!!.name.lowercase()}" else "^${subIdentifier!!.joinToString(".")}"
|
||||
BaseDataType.ARRAY_POINTER -> if(sub!=null) "^${sub!!.name.lowercase()}[$lengthStr]" else "^${subIdentifier!!.joinToString(".")}[$lengthStr]"
|
||||
BaseDataType.ARRAY -> {
|
||||
when(this.sub) {
|
||||
BaseDataType.UBYTE -> "ubyte[$lengthStr]"
|
||||
@@ -342,6 +344,9 @@ internal fun parseRegisterOrStatusflag(sourceregs: String): RegisterOrStatusflag
|
||||
|
||||
|
||||
fun irType(type: DataType): IRDataType {
|
||||
if(type.isPointer)
|
||||
return IRDataType.WORD // TODO do we need typed pointers in IR? probably not though?
|
||||
|
||||
if(type.base.isPassByRef)
|
||||
return IRDataType.WORD
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@ block: identifier integerliteral? EOL? '{' EOL? (block_statement | EOL)* '}';
|
||||
block_statement:
|
||||
directive
|
||||
| variabledeclaration
|
||||
| structdeclaration
|
||||
| subroutinedeclaration
|
||||
| inlineasm
|
||||
| inlineir
|
||||
@@ -88,6 +89,7 @@ block_statement:
|
||||
statement :
|
||||
directive
|
||||
| variabledeclaration
|
||||
| structdeclaration
|
||||
| assignment
|
||||
| augassignment
|
||||
| unconditionaljump
|
||||
@@ -121,6 +123,13 @@ variabledeclaration :
|
||||
;
|
||||
|
||||
|
||||
structdeclaration:
|
||||
'struct' identifier '{' EOL? (structfielddecl | EOL)+ '}'
|
||||
;
|
||||
|
||||
structfielddecl: datatype identifier;
|
||||
|
||||
|
||||
subroutinedeclaration :
|
||||
subroutine
|
||||
| asmsubroutine
|
||||
@@ -153,7 +162,11 @@ constdecl: 'const' varinitializer ;
|
||||
|
||||
memoryvardecl: ADDRESS_OF varinitializer;
|
||||
|
||||
datatype: 'ubyte' | 'byte' | 'uword' | 'word' | 'long' | 'float' | 'str' | 'bool' ;
|
||||
basedatatype: 'ubyte' | 'byte' | 'uword' | 'word' | 'long' | 'float' | 'str' | 'bool' ;
|
||||
|
||||
datatype: pointertype | basedatatype;
|
||||
|
||||
pointertype: '^' (scoped_identifier | basedatatype);
|
||||
|
||||
arrayindex: '[' expression ']' ;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package prog8.code
|
||||
import prog8.code.ast.PtAsmSub
|
||||
import prog8.code.ast.PtNode
|
||||
import prog8.code.ast.PtProgram
|
||||
import prog8.code.ast.PtStructDecl
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
@@ -107,7 +108,8 @@ enum class StNodeType {
|
||||
MEMVAR,
|
||||
CONSTANT,
|
||||
BUILTINFUNC,
|
||||
MEMORYSLAB
|
||||
MEMORYSLAB,
|
||||
STRUCT
|
||||
}
|
||||
|
||||
|
||||
@@ -249,6 +251,13 @@ class StMemVar(name: String,
|
||||
}
|
||||
}
|
||||
|
||||
class StStruct(
|
||||
name: String,
|
||||
val members: List<Pair<DataType, String>>,
|
||||
astNode: PtStructDecl?
|
||||
) : StNode(name, StNodeType.STRUCT, astNode)
|
||||
|
||||
|
||||
class StMemorySlab(
|
||||
name: String,
|
||||
val size: UInt,
|
||||
|
||||
@@ -60,6 +60,9 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
||||
val params = node.parameters.map {StSubroutineParameter(it.name, it.type, it.register) }
|
||||
StSub(node.name, params, node.returns, node)
|
||||
}
|
||||
is PtStructDecl -> {
|
||||
StStruct(node.name, node.members, node)
|
||||
}
|
||||
is PtVariable -> {
|
||||
val initialNumeric: Double?
|
||||
val initialString: StString?
|
||||
@@ -134,9 +137,10 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
||||
return value.children.map {
|
||||
when(it) {
|
||||
is PtAddressOf -> {
|
||||
if(it.isFromArrayElement)
|
||||
TODO("address-of array element $it in initial array value")
|
||||
StArrayElement(null, it.identifier.name, null)
|
||||
when {
|
||||
it.isFromArrayElement -> TODO("address-of array element $it in initial array value")
|
||||
else -> StArrayElement(null, it.identifier.name, null)
|
||||
}
|
||||
}
|
||||
is PtNumber -> StArrayElement(it.number, null, null)
|
||||
is PtBool -> StArrayElement(null, null, it.value)
|
||||
|
||||
@@ -25,7 +25,9 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
|
||||
is PtAddressOf -> {
|
||||
if(other !is PtAddressOf)
|
||||
return false
|
||||
if (other.type!==type || !(other.identifier isSameAs identifier))
|
||||
if (other.type!==type)
|
||||
return false
|
||||
if(!(other.identifier isSameAs identifier))
|
||||
return false
|
||||
if(other.children.size!=children.size)
|
||||
return false
|
||||
@@ -157,7 +159,7 @@ class PtArrayIndexer(elementType: DataType, position: Position): PtExpression(el
|
||||
get() = variable.type.isSplitWordArray
|
||||
|
||||
init {
|
||||
require(elementType.isNumericOrBool)
|
||||
require(elementType.isNumericOrBool || elementType.isPointer)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,12 +378,12 @@ class PtString(val value: String, val encoding: Encoding, position: Position) :
|
||||
}
|
||||
|
||||
|
||||
class PtTypeCast(type: BaseDataType, position: Position) : PtExpression(DataType.forDt(type), position) {
|
||||
class PtTypeCast(type: DataType, position: Position) : PtExpression(type, position) {
|
||||
val value: PtExpression
|
||||
get() = children.single() as PtExpression
|
||||
|
||||
fun copy(): PtTypeCast {
|
||||
val copy = PtTypeCast(type.base, position)
|
||||
val copy = PtTypeCast(type, position)
|
||||
if(children[0] is PtIdentifier) {
|
||||
copy.add((children[0] as PtIdentifier).copy())
|
||||
} else {
|
||||
|
||||
@@ -172,6 +172,9 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
|
||||
is PtDefer -> "<defer>"
|
||||
is PtIfExpression -> "<ifexpr>"
|
||||
is PtJmpTable -> "<jmptable>"
|
||||
is PtStructDecl -> {
|
||||
"struct ${node.name} { " + node.members.joinToString(" ") { "${it.first} ${it.second}" } + " }"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -221,6 +221,9 @@ class PtMemMapped(name: String, override val type: DataType, val address: UInt,
|
||||
}
|
||||
|
||||
|
||||
class PtStructDecl(name: String, val members: List<Pair<DataType, String>>, position: Position) : PtNamedNode(name, position)
|
||||
|
||||
|
||||
class PtWhen(position: Position) : PtNode(position) {
|
||||
val value: PtExpression
|
||||
get() = children[0] as PtExpression
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
</options>
|
||||
<keywords keywords="&;&<;&>;->;@;alias;and;as;asmsub;break;clobbers;continue;do;downto;else;extsub;false;for;goto;if;if_cc;if_cs;if_eq;if_mi;if_ne;if_neg;if_nz;if_pl;if_pos;if_vc;if_vs;if_z;in;inline;not;or;repeat;return;step;sub;to;true;unroll;until;when;while;xor;~" ignore_case="false" />
|
||||
<keywords2 keywords="%address;%align;%asm;%asmbinary;%asminclude;%breakpoint;%encoding;%import;%ir;%jmptable;%launcher;%memtop;%option;%output;%zeropage;%zpallowed;%zpreserved;@align64;@alignpage;@alignword;@bank;@dirty;@nosplit;@nozp;@requirezp;@shared;@split;@zp;atascii:;c64os:;cp437:;default:;iso16:;iso5:;iso:;kata:;petscii:;sc:" />
|
||||
<keywords3 keywords="bool;byte;const;float;long;str;ubyte;uword;void;word" />
|
||||
<keywords3 keywords="^bool;^byte;^float;^str;^ubyte;^uword;^word;bool;byte;const;float;long;str;struct;ubyte;uword;void;word" />
|
||||
<keywords4 keywords="abs;bmx;call;callfar;callfar2;cbm;clamp;cmp;conv;cx16;defer;diskio;divmod;floats;len;lsb;lsw;math;max;memory;min;mkword;msb;msw;peek;peekf;peekw;poke;pokef;pokew;psg;rol;rol2;ror;ror2;rrestore;rrestorex;rsave;rsavex;setlsb;setmsb;sgn;sizeof;sqrt;strings;sys;txt;verafx" />
|
||||
</highlighting>
|
||||
<extensionMap>
|
||||
<mapping ext="prog8" />
|
||||
<mapping ext="p8" />
|
||||
<mapping ext="prog8" />
|
||||
</extensionMap>
|
||||
</filetype>
|
||||
</filetype>
|
||||
@@ -24,7 +24,7 @@
|
||||
<Keywords name="Folders in comment, open"></Keywords>
|
||||
<Keywords name="Folders in comment, middle"></Keywords>
|
||||
<Keywords name="Folders in comment, close"></Keywords>
|
||||
<Keywords name="Keywords1">void const
str
byte ubyte bool
long word uword
float
zp shared split nosplit requirezp nozp</Keywords>
|
||||
<Keywords name="Keywords1">void const
str
byte ubyte bool
long word uword
float
zp shared split nosplit requirezp nozp struct</Keywords>
|
||||
<Keywords name="Keywords2">%address
%asm
%ir
%asmbinary
%asminclude
%align
%breakpoint
%encoding
%import
%jmptable
%memtop
%launcher
%option
%output
%zeropage
%zpreserved
%zpallowed</Keywords>
|
||||
<Keywords name="Keywords3">inline sub asmsub extsub
clobbers
asm
if
when else
if_cc if_cs if_eq if_mi if_neg if_nz if_pl if_pos if_vc if_vs if_z
for in step do while repeat unroll
break continue return goto</Keywords>
|
||||
<Keywords name="Keywords4">alias abs call callfar callfar2 clamp cmp defer divmod len lsb lsw lsl lsr memory mkword min max msb msw peek peekw peekf poke pokew pokef rsave rsavex rrestore rrestorex rnd rndw rol rol2 ror ror2 setlsb setmsb sgn sizeof sqrtw</Keywords>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<Keywords name="Folders in comment, open"></Keywords>
|
||||
<Keywords name="Folders in comment, middle"></Keywords>
|
||||
<Keywords name="Folders in comment, close"></Keywords>
|
||||
<Keywords name="Keywords1">void const
str
byte ubyte bool
long word uword
float
zp shared split nosplit requirezp nozp</Keywords>
|
||||
<Keywords name="Keywords1">void const
str
byte ubyte bool
long word uword
float
zp shared split nosplit requirezp nozp struct</Keywords>
|
||||
<Keywords name="Keywords2">%address
%asm
%ir
%asmbinary
%asminclude
%align
%breakpoint
%encoding
%import
%jmptable
%memtop
%launcher
%option
%output
%zeropage
%zpreserved
%zpallowed</Keywords>
|
||||
<Keywords name="Keywords3">inline sub asmsub extsub
clobbers
asm
if
when else
if_cc if_cs if_eq if_mi if_neg if_nz if_pl if_pos if_vc if_vs if_z
for in step do while repeat unroll
break continue return goto</Keywords>
|
||||
<Keywords name="Keywords4">alias abs call callfar callfar2 clamp cmp defer divmod len lsb lsw lsl lsr memory mkword min max msb msw peek peekw peekf poke pokew pokef rsave rsavex rrestore rrestorex rnd rndw rol rol2 ror ror2 setlsb setmsb sgn sizeof sqrtw</Keywords>
|
||||
|
||||
@@ -149,7 +149,7 @@ contexts:
|
||||
- match: (\b\w+\.)
|
||||
scope: entity.name.namespace.prog8
|
||||
storage:
|
||||
- match: (\b(ubyte|byte|word|uword|long|float|str)\b)
|
||||
- match: (\b(ubyte|byte|word|uword|long|float|str|struct)\b)
|
||||
scope: storage.type.prog8
|
||||
- match: (\b(const)\b)
|
||||
scope: storage.modifier.prog8
|
||||
|
||||
@@ -198,10 +198,14 @@ class VmProgramLoader {
|
||||
|
||||
// zero out uninitialized ('bss') variables.
|
||||
if(variable.uninitialized) {
|
||||
if(variable.dt.isArray) {
|
||||
val dt = variable.dt
|
||||
val dt = variable.dt
|
||||
if(dt.isArray) {
|
||||
repeat(variable.length!!) {
|
||||
when {
|
||||
dt.isPointerArray -> {
|
||||
memory.setUW(addr, 0u) // array of pointers is just array of word addresses
|
||||
addr += 2
|
||||
}
|
||||
dt.isString || dt.isBoolArray || dt.isByteArray -> {
|
||||
memory.setUB(addr, 0u)
|
||||
addr++
|
||||
@@ -225,11 +229,11 @@ class VmProgramLoader {
|
||||
}
|
||||
} else {
|
||||
when {
|
||||
variable.dt.isUnsignedByte || variable.dt.isBool -> memory.setUB(addr, 0u)
|
||||
variable.dt.isSignedByte -> memory.setSB(addr, 0)
|
||||
variable.dt.isUnsignedWord -> memory.setUW(addr, 0u)
|
||||
variable.dt.isSignedWord -> memory.setSW(addr, 0)
|
||||
variable.dt.isFloat -> memory.setFloat(addr, 0.0)
|
||||
dt.isUnsignedByte || dt.isBool -> memory.setUB(addr, 0u)
|
||||
dt.isSignedByte -> memory.setSB(addr, 0)
|
||||
dt.isUnsignedWord || dt.isPointer -> memory.setUW(addr, 0u)
|
||||
dt.isSignedWord -> memory.setSW(addr, 0)
|
||||
dt.isFloat -> memory.setFloat(addr, 0.0)
|
||||
else -> throw IRParseException("invalid dt")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package prog8.vm
|
||||
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.IMemSizer
|
||||
import prog8.code.core.IStringEncoding
|
||||
import prog8.code.core.InternalCompilerException
|
||||
@@ -19,6 +20,7 @@ internal class VmVariableAllocator(st: IRSymbolTable, val encoding: IStringEncod
|
||||
for (variable in st.allVariables()) {
|
||||
val memsize =
|
||||
when {
|
||||
variable.dt.isPointer -> memsizer.memorySize(DataType.UWORD, null) // a pointer is just a word address
|
||||
variable.dt.isString -> variable.onetimeInitializationStringValue!!.first.length + 1 // include the zero byte
|
||||
variable.dt.isNumericOrBool -> memsizer.memorySize(variable.dt, null)
|
||||
variable.dt.isArray -> memsizer.memorySize(variable.dt, variable.length!!)
|
||||
|
||||
Reference in New Issue
Block a user