mirror of
https://github.com/irmen/prog8.git
synced 2026-04-21 17:16:33 +00:00
preparing for statically allocating struct instances
This commit is contained in:
@@ -227,6 +227,7 @@ private fun PtIdentifier.prefix(parent: PtNode, st: SymbolTable): PtIdentifier {
|
||||
StNodeType.CONSTANT -> 'c'
|
||||
StNodeType.BUILTINFUNC -> 's'
|
||||
StNodeType.MEMORYSLAB -> 'v'
|
||||
StNodeType.STRUCTINSTANCE -> 'i'
|
||||
else -> '?'
|
||||
}
|
||||
val newName = prefixScopedName(name, prefixType)
|
||||
|
||||
@@ -44,6 +44,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
"setlsb" -> funcSetLsbMsb(fcall, false)
|
||||
"setmsb" -> funcSetLsbMsb(fcall, true)
|
||||
"memory" -> funcMemory(fcall, discardResult, resultRegister)
|
||||
"structalloc" -> funcStructAlloc(fcall, discardResult, resultRegister)
|
||||
"peekw" -> funcPeekW(fcall, resultRegister)
|
||||
"peekf" -> funcPeekF(fcall, resultRegister)
|
||||
"peek" -> throw AssemblyError("peek() should have been replaced by @()")
|
||||
@@ -390,6 +391,16 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.translateNormalAssignment(assign, fcall.definingISub())
|
||||
}
|
||||
|
||||
private fun funcStructAlloc(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultRegister: RegisterOrPair?) {
|
||||
if(discardResult)
|
||||
throw AssemblyError("should not discard result of struct allocation at $fcall")
|
||||
if(fcall.args.isEmpty())
|
||||
TODO("struct alloc in BSS")
|
||||
else
|
||||
TODO("static struct alloc with values")
|
||||
}
|
||||
|
||||
|
||||
private fun funcSqrt(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?, scope: IPtSubroutine?) {
|
||||
translateArguments(fcall, scope)
|
||||
when(fcall.args[0].type.base) {
|
||||
|
||||
@@ -45,10 +45,19 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
"prog8_lib_stringcompare" -> funcStringCompare(call)
|
||||
"prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE)
|
||||
"prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD)
|
||||
"structalloc" -> funcStructAlloc(call)
|
||||
else -> throw AssemblyError("missing builtinfunc for ${call.name}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcStructAlloc(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val pointerReg = codeGen.registers.next(IRDataType.WORD)
|
||||
val address = 65534 // TODO determine correct address!!
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1 = pointerReg, immediate = address), null)
|
||||
return ExpressionCodeResult(result, IRDataType.WORD, pointerReg, -1)
|
||||
}
|
||||
|
||||
private fun funcSquare(call: PtBuiltinFunctionCall, resultType: IRDataType): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val valueTr = exprGen.translateExpression(call.args[0])
|
||||
|
||||
@@ -14,6 +14,7 @@ fun convertStToIRSt(sourceSt: SymbolTable?): IRSymbolTable {
|
||||
StNodeType.MEMVAR -> st.add(convert(it.value as StMemVar))
|
||||
StNodeType.CONSTANT -> st.add(convert(it.value as StConstant))
|
||||
StNodeType.MEMORYSLAB -> st.add(convert(it.value as StMemorySlab))
|
||||
StNodeType.STRUCTINSTANCE -> st.add(convert(it.value as StStructInstance))
|
||||
else -> { }
|
||||
}
|
||||
}
|
||||
@@ -37,12 +38,19 @@ fun convertStToIRSt(sourceSt: SymbolTable?): IRSymbolTable {
|
||||
}
|
||||
|
||||
|
||||
private fun convert(variable: StStaticVariable): IRStStaticVariable {
|
||||
private fun convert(instance: StStructInstance): IRStStructInstance {
|
||||
val values = instance.initialValues.map { convertArrayElt(it) }
|
||||
return IRStStructInstance(instance.name, values, instance.size.toInt())
|
||||
}
|
||||
|
||||
fun convertArrayElt(elt: StArrayElement): IRStArrayElement = if(elt.boolean!=null)
|
||||
IRStArrayElement(elt.boolean, null, elt.addressOfSymbol)
|
||||
else
|
||||
IRStArrayElement(null, elt.number, elt.addressOfSymbol)
|
||||
|
||||
private fun convertArrayElt(elt: StArrayElement): IRStArrayElement = if(elt.boolean!=null)
|
||||
IRStArrayElement(elt.boolean, null, elt.addressOfSymbol)
|
||||
else
|
||||
IRStArrayElement(null, elt.number, elt.addressOfSymbol)
|
||||
|
||||
|
||||
private fun convert(variable: StStaticVariable): IRStStaticVariable {
|
||||
|
||||
if('.' in variable.name) {
|
||||
return IRStStaticVariable(variable.name,
|
||||
|
||||
@@ -1169,7 +1169,7 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
if(array.parent is VarDecl) {
|
||||
if (!array.value.all { it is NumericLiteral || it is AddressOf })
|
||||
errors.err("array literal for variable initialization contains non-constant elements", array.position)
|
||||
errors.err("initialization list contains non-constant elements", array.value[0].position)
|
||||
} else if(array.parent is ForLoop) {
|
||||
if (!array.value.all { it.constValue(program) != null })
|
||||
errors.err("array literal for iteration must contain constants. Try using a separate array variable instead?", array.position)
|
||||
@@ -1721,6 +1721,25 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
if(target is StructDecl) {
|
||||
// it's a static struct inializer, check the values
|
||||
if(args.size!=0) {
|
||||
if (!args.all { it is NumericLiteral || it is AddressOf || (it is TypecastExpression && it.expression is NumericLiteral)})
|
||||
errors.err("initialization list contains non-constant elements", args[0].position)
|
||||
if (target.fields.size != args.size)
|
||||
errors.err("initialization list needs to be same number of values as the struct has fields: expected ${target.fields.size} or 0, got ${args.size}", args[0].position)
|
||||
else
|
||||
target.fields.zip(args).withIndex().forEach { (index, fv) ->
|
||||
val (field, value) = fv
|
||||
val valueDt = value.inferType(program)
|
||||
if(valueDt isNotAssignableTo field.first) {
|
||||
errors.err("value #${index+1} has incompatible type $valueDt for field '${field.second}' (${field.first})", value.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO rest
|
||||
}
|
||||
|
||||
args.forEach{
|
||||
checkLongType(it)
|
||||
}
|
||||
@@ -1904,8 +1923,8 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(struct: StructDecl) {
|
||||
val uniqueFields = struct.members.map { it.second }.toSet()
|
||||
if(uniqueFields.size!=struct.members.size)
|
||||
val uniqueFields = struct.fields.map { it.second }.toSet()
|
||||
if(uniqueFields.size!=struct.fields.size)
|
||||
errors.err("duplicate field names in struct", struct.position)
|
||||
val memsize = struct.memsize(program.memsizer)
|
||||
if(memsize>256)
|
||||
@@ -2161,7 +2180,7 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
if (!correct) {
|
||||
if (value.parent is VarDecl && !value.value.all { it is NumericLiteral || it is AddressOf })
|
||||
errors.err("array literal for variable initialization contains non-constant elements", value.position)
|
||||
errors.err("initialization list contains non-constant elements", value.value[0].position)
|
||||
else
|
||||
errors.err("array element out of range for type $targetDt", value.position)
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ class AstPreprocessor(val program: Program,
|
||||
|
||||
override fun after(struct: StructDecl, parent: Node): Iterable<IAstModification> {
|
||||
// convert all antlr names to structs
|
||||
struct.members.forEach {
|
||||
struct.fields.forEach {
|
||||
if(it.first.subTypeFromAntlr!=null) {
|
||||
val struct = struct.definingScope.lookup(it.first.subTypeFromAntlr!!) as? ISubType
|
||||
if(struct!=null)
|
||||
|
||||
@@ -371,7 +371,7 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
// a call to a struct yields a struct instance and means: allocate a statically initialized struct instance of that type
|
||||
val struct = iType.getOrUndef().subType!!
|
||||
val pointertype = DataType.pointerToType(struct)
|
||||
PtBuiltinFunctionCall("staticalloc", false, true, pointertype, srcCall.position)
|
||||
PtBuiltinFunctionCall("structalloc", false, true, pointertype, srcCall.position)
|
||||
} else {
|
||||
// regular function call
|
||||
PtFunctionCall(target, iType.isUnknown && srcCall.parent !is Assignment, iType.getOrElse { DataType.UNDEFINED }, srcCall.position)
|
||||
@@ -625,7 +625,7 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
}
|
||||
|
||||
private fun transform(struct: StructDecl): PtStructDecl {
|
||||
return PtStructDecl(struct.name, struct.members, struct.position)
|
||||
return PtStructDecl(struct.name, struct.fields, struct.position)
|
||||
}
|
||||
|
||||
private fun transform(srcWhen: When): PtWhen {
|
||||
|
||||
@@ -33,7 +33,7 @@ private fun processSubtypesIntoStReferences(program: PtProgram, st: SymbolTable)
|
||||
when(node) {
|
||||
is IPtVariable -> fixSubtype(node.type)
|
||||
is PtPointerDeref -> fixSubtype(node.type)
|
||||
is PtStructDecl -> node.members.forEach { fixSubtype(it.first) }
|
||||
is PtStructDecl -> node.fields.forEach { fixSubtype(it.first) }
|
||||
is PtAsmSub -> node.returns.forEach { fixSubtype(it.second) }
|
||||
is PtExpression -> fixSubtype(node.type)
|
||||
is PtSubSignature -> node.returns.forEach { fixSubtype(it) }
|
||||
|
||||
@@ -71,7 +71,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
return noModifications
|
||||
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
addTypecastOrCastedValueModification(modifications, declValue, decl.datatype.base, decl)
|
||||
addTypecastOrCastedValueModification(modifications, declValue, decl.datatype, decl)
|
||||
return modifications
|
||||
}
|
||||
}
|
||||
@@ -180,8 +180,8 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
} else {
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
when {
|
||||
toFix===expr.left -> addTypecastOrCastedValueModification(modifications, expr.left, commonDt.base, expr)
|
||||
toFix===expr.right -> addTypecastOrCastedValueModification(modifications, expr.right, commonDt.base, expr)
|
||||
toFix===expr.left -> addTypecastOrCastedValueModification(modifications, expr.left, commonDt, expr)
|
||||
toFix===expr.right -> addTypecastOrCastedValueModification(modifications, expr.right, commonDt, expr)
|
||||
else -> throw FatalAstException("confused binary expression side")
|
||||
}
|
||||
return modifications
|
||||
@@ -240,7 +240,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
// special case, don't typecast STR/arrays to UWORD, we support those assignments "directly"
|
||||
return noModifications
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
addTypecastOrCastedValueModification(modifications, assignment.value, targettype.base, assignment)
|
||||
addTypecastOrCastedValueModification(modifications, assignment.value, targettype, assignment)
|
||||
return modifications
|
||||
} else {
|
||||
fun castLiteral(cvalue2: NumericLiteral): List<IAstModification.ReplaceNode> {
|
||||
@@ -274,11 +274,13 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
val params = when(val sub = call.target.targetStatement(program)) {
|
||||
is BuiltinFunctionPlaceholder -> BuiltinFunctions.getValue(sub.name).parameters.toList()
|
||||
is Subroutine -> sub.parameters.map { FParam(it.name, it.type.base) }
|
||||
is StructDecl -> sub.fields.map { FParam(it.second, it.first.base) }
|
||||
else -> emptyList()
|
||||
}
|
||||
|
||||
params.zip(call.args).forEach {
|
||||
val targetDt = it.first.possibleDatatypes.first()
|
||||
val possibleTargetDt = it.first.possibleDatatypes.first()
|
||||
val targetDt = if(possibleTargetDt.isPointer) BaseDataType.UWORD else possibleTargetDt // use UWORD instead of a pointer type (using words for pointers is allowed without further casting)
|
||||
val argIdt = it.second.inferType(program)
|
||||
if (argIdt.isKnown) {
|
||||
val argDt = argIdt.getOrUndef()
|
||||
@@ -286,7 +288,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
val identifier = it.second as? IdentifierReference
|
||||
val number = it.second as? NumericLiteral
|
||||
if(number!=null) {
|
||||
addTypecastOrCastedValueModification(modifications, it.second, targetDt, call as Node)
|
||||
addTypecastOrCastedValueModification(modifications, it.second, DataType.forDt(targetDt), call as Node)
|
||||
} else if(identifier!=null && targetDt==BaseDataType.UWORD && argDt.isPassByRef) {
|
||||
if(!identifier.isSubroutineParameter()) {
|
||||
// We allow STR/ARRAY values for UWORD parameters.
|
||||
@@ -300,10 +302,10 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
}
|
||||
}
|
||||
} else if(targetDt==BaseDataType.BOOL) {
|
||||
addTypecastOrCastedValueModification(modifications, it.second, BaseDataType.BOOL, call as Node)
|
||||
addTypecastOrCastedValueModification(modifications, it.second, DataType.BOOL, call as Node)
|
||||
} else if(!targetDt.isIterable && argDt isAssignableTo DataType.forDt(targetDt)) {
|
||||
if(!argDt.isString || targetDt!=BaseDataType.UWORD)
|
||||
addTypecastOrCastedValueModification(modifications, it.second, targetDt, call as Node)
|
||||
addTypecastOrCastedValueModification(modifications, it.second, DataType.forDt(targetDt), call as Node)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -342,7 +344,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
if(castedValue!=null)
|
||||
modifications += IAstModification.ReplaceNode(memread.addressExpression, castedValue, memread)
|
||||
else
|
||||
addTypecastOrCastedValueModification(modifications, memread.addressExpression, BaseDataType.UWORD, memread)
|
||||
addTypecastOrCastedValueModification(modifications, memread.addressExpression, DataType.UWORD, memread)
|
||||
}
|
||||
return modifications
|
||||
}
|
||||
@@ -356,7 +358,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
if(castedValue!=null)
|
||||
modifications += IAstModification.ReplaceNode(memwrite.addressExpression, castedValue, memwrite)
|
||||
else
|
||||
addTypecastOrCastedValueModification(modifications, memwrite.addressExpression, BaseDataType.UWORD, memwrite)
|
||||
addTypecastOrCastedValueModification(modifications, memwrite.addressExpression, DataType.UWORD, memwrite)
|
||||
}
|
||||
return modifications
|
||||
}
|
||||
@@ -396,7 +398,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
}
|
||||
}
|
||||
} else {
|
||||
addTypecastOrCastedValueModification(modifications, returnValue, subReturnType.base, returnStmt)
|
||||
addTypecastOrCastedValueModification(modifications, returnValue, subReturnType, returnStmt)
|
||||
continue
|
||||
}
|
||||
}
|
||||
@@ -471,7 +473,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
)
|
||||
if (toFix != null) {
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
addTypecastOrCastedValueModification(modifications, toFix, commonDt.base, ifExpr)
|
||||
addTypecastOrCastedValueModification(modifications, toFix, commonDt, ifExpr)
|
||||
return modifications
|
||||
}
|
||||
}
|
||||
@@ -600,7 +602,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
|
||||
val (commonDt, toChange) = BinaryExpression.commonDatatype(fromDt, toDt, range.from, range.to)
|
||||
if(toChange!=null)
|
||||
addTypecastOrCastedValueModification(modifications, toChange, commonDt.base, range)
|
||||
addTypecastOrCastedValueModification(modifications, toChange, commonDt, range)
|
||||
|
||||
return modifications
|
||||
}
|
||||
@@ -622,13 +624,13 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
private fun addTypecastOrCastedValueModification(
|
||||
modifications: MutableList<IAstModification>,
|
||||
expressionToCast: Expression,
|
||||
requiredType: BaseDataType, // TODO DataType?
|
||||
requiredType: DataType,
|
||||
parent: Node
|
||||
) {
|
||||
val sourceDt = expressionToCast.inferType(program).getOrUndef()
|
||||
if(sourceDt.base == requiredType)
|
||||
if(sourceDt.base == requiredType.base)
|
||||
return
|
||||
if(requiredType == BaseDataType.BOOL) {
|
||||
if(requiredType.isBool) {
|
||||
if(sourceDt.isNumeric || sourceDt.isPointer) {
|
||||
// only allow numerics and pointers to be implicitly cast to bool
|
||||
val cast = TypecastExpression(expressionToCast, DataType.BOOL, true, expressionToCast.position)
|
||||
@@ -641,8 +643,8 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
if(requiredType.isPointer && sourceDt.isUnsignedWord)
|
||||
return
|
||||
|
||||
if(expressionToCast is NumericLiteral && expressionToCast.type!=BaseDataType.FLOAT) { // refuse to automatically truncate floats
|
||||
val castedValue = expressionToCast.cast(requiredType, true)
|
||||
if(expressionToCast is NumericLiteral && expressionToCast.type!=BaseDataType.FLOAT && requiredType.isNumericOrBool) { // refuse to automatically truncate floats
|
||||
val castedValue = expressionToCast.cast(requiredType.base, true)
|
||||
if (castedValue.isValid) {
|
||||
val signOriginal = sign(expressionToCast.number)
|
||||
val signCasted = sign(castedValue.valueOrZero().number)
|
||||
@@ -653,7 +655,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
}
|
||||
}
|
||||
|
||||
val cast = TypecastExpression(expressionToCast, DataType.forDt(requiredType), true, expressionToCast.position)
|
||||
val cast = TypecastExpression(expressionToCast, requiredType, true, expressionToCast.position)
|
||||
modifications += IAstModification.ReplaceNode(expressionToCast, cast, parent)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,9 +155,14 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
if (typecast.type.isBool) {
|
||||
val et = typecast.expression.inferType(program)
|
||||
if (et.isNumeric) {
|
||||
val zero = defaultZero(et.getOrUndef().base, typecast.position)
|
||||
val cmp = BinaryExpression(typecast.expression, "!=", zero, typecast.position)
|
||||
return listOf(IAstModification.ReplaceNode(typecast, cmp, parent))
|
||||
if(typecast.expression is NumericLiteral) {
|
||||
val boolean = NumericLiteral.fromBoolean((typecast.expression as NumericLiteral).asBooleanValue, typecast.expression.position)
|
||||
return listOf(IAstModification.ReplaceNode(typecast, boolean, parent))
|
||||
} else {
|
||||
val zero = defaultZero(et.getOrUndef().base, typecast.position)
|
||||
val cmp = BinaryExpression(typecast.expression, "!=", zero, typecast.position)
|
||||
return listOf(IAstModification.ReplaceNode(typecast, cmp, parent))
|
||||
}
|
||||
}
|
||||
else if (et.isPointer) {
|
||||
val ptrAsUword = TypecastExpression(typecast.expression, DataType.UWORD, true, typecast.position)
|
||||
|
||||
@@ -158,7 +158,7 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
|
||||
|
||||
override fun visit(struct: StructDecl) {
|
||||
outputln("struct ${struct.name} {")
|
||||
for(member in struct.members) {
|
||||
for(member in struct.fields) {
|
||||
outputlni( " ${member.first} ${member.second}")
|
||||
}
|
||||
outputlni("}")
|
||||
|
||||
@@ -78,8 +78,8 @@ private fun VariabledeclarationContext.toAst() : VarDecl {
|
||||
|
||||
private fun StructdeclarationContext.toAst(): Statement {
|
||||
val name = identifier().text
|
||||
val members: List<Pair<DataType, List<String>>> = structfielddecl().map { it.toAst() }
|
||||
val flattened = members.flatMap { (dt, names) -> names.map { dt to it}}
|
||||
val fields: List<Pair<DataType, List<String>>> = structfielddecl().map { it.toAst() }
|
||||
val flattened = fields.flatMap { (dt, names) -> names.map { dt to it}}
|
||||
return StructDecl(name, flattened, toPosition())
|
||||
}
|
||||
|
||||
|
||||
@@ -374,7 +374,7 @@ class VarDecl(val type: VarDeclType,
|
||||
}
|
||||
}
|
||||
|
||||
class StructDecl(override val name: String, val members: List<Pair<DataType, String>>, override val position: Position) : Statement(), INamedStatement, ISubType {
|
||||
class StructDecl(override val name: String, val fields: List<Pair<DataType, String>>, override val position: Position) : Statement(), INamedStatement, ISubType {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
@@ -383,11 +383,11 @@ class StructDecl(override val name: String, val members: List<Pair<DataType, Str
|
||||
|
||||
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 copy() = StructDecl(name, fields.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 getFieldType(name: String): DataType? = members.firstOrNull { it.second==name }?.first
|
||||
fun memsize(sizer: IMemSizer): Int = fields.sumOf { sizer.memorySize(it.first, 1) }
|
||||
fun getFieldType(name: String): DataType? = fields.firstOrNull { it.second==name }?.first
|
||||
override val scopedNameString by lazy { scopedName.joinToString(".") }
|
||||
}
|
||||
|
||||
|
||||
@@ -28,16 +28,18 @@ STRUCTS and TYPED POINTERS
|
||||
- DONE (for basic types only): allow array syntax on pointers too: ptr[2] means ptr+sizeof()*2, ptr[0] just means ptr^^ .
|
||||
- DONE (?) allow array syntax on pointers to structs too, but what type will ptr[2] have? And it will require ptr[2].field to work as well now. Actually that will be the only thing to work for now.
|
||||
- DONE: allow multi-field declarations in structs
|
||||
- are the ARRAY_POINTER and ARRAY_STRUCT data type enums realy needed? can just use ARRAY?
|
||||
- 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 could be: ^^Node ptr = Node(1,2,3,4) statically allocates a Node with fields set to 1,2,3,4 and puts the address in ptr.
|
||||
- Node() without arguments could allocate a node in BSS variable space instead.
|
||||
- What about static initialization of an array of struct pointers?
|
||||
- support @dirty on pointer vars
|
||||
- pointer types in subroutine signatures (both normal and asm-subs)
|
||||
- support chaining pointer dereference on function calls that return a pointer. (type checking now fails on stuff like func().field and func().next.field)
|
||||
- are the ARRAY_POINTER and ARRAY_STRUCT data type enums realy needed? can't we just use ARRAY?
|
||||
- pointer arithmetic should follow C: ptr=ptr+10 adds 10*sizeof() instead of just 10.
|
||||
- fixing the pointer dereferencing issues (cursed hybrid beween IdentifierReference, PtrDereferece and PtrIndexedDereference) may require getting rid of scoped identifiers altogether and treat '.' as a "scope or pointer following operator"
|
||||
- add unit tests for all changes
|
||||
- arrays of structs? No -> 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.
|
||||
- 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 could be: ^^Node ptr = Node(1,2,3,4) statically allocates a Node with fields set to 1,2,3,4 and puts the address in ptr.
|
||||
- Verify the argments to such a static struct initializer 'call' against the fields, and the same check as for static array values (const value or address-of) ?
|
||||
- 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..?)
|
||||
@@ -73,7 +75,7 @@ Future Things and Ideas
|
||||
|
||||
IR/VM
|
||||
-----
|
||||
- adding LOADFIELD/STOREFIELD instructions that encode the field offset as immediate value so we avoid a separate ADD instruction to calculate the address
|
||||
- add LOADFIELD/STOREFIELD instructions that encode the field offset as immediate value so we avoid a separate ADD instruction to calculate the address
|
||||
- getting it in shape for code generation...: the IR file should be able to encode every detail about a prog8 program (the VM doesn't have to actually be able to run all of it though!)
|
||||
- fix call() return value handling (... what's wrong with it again?)
|
||||
- encode asmsub/extsub clobber info in the call , or maybe include these definitions in the p8ir file itself too. (return registers are already encoded in the CALL instruction)
|
||||
|
||||
+9
-4
@@ -1,4 +1,5 @@
|
||||
%zeropage basicsafe
|
||||
%import math
|
||||
%import textio
|
||||
|
||||
main {
|
||||
@@ -12,14 +13,18 @@ main {
|
||||
; these in turn point to the 7th, 8th and 9th.
|
||||
|
||||
struct Node {
|
||||
ubyte value, value2, value3
|
||||
bool flag
|
||||
ubyte value
|
||||
uword value2
|
||||
^^Node next
|
||||
}
|
||||
|
||||
; proposed static initializer syntax:
|
||||
; ^^Node @shared node3 = Node( 33,2,3, 0 )
|
||||
; ^^Node @shared node2 = Node( 22,2,3, &node3 )
|
||||
; ^^Node @shared node1 = Node( 11,2,3, &node2 )
|
||||
^^Node @shared node3 = Node( true,222,333, 0 )
|
||||
^^Node @shared node2 = Node( false,2,3, &node3 )
|
||||
^^Node @shared node1 = Node( false,2,3, &node2 )
|
||||
^^Node @shared node0 = Node()
|
||||
|
||||
|
||||
^^Node n0,n1,n2,n3,n4,n5,n6,n7,n8
|
||||
|
||||
|
||||
@@ -342,6 +342,15 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
|
||||
xml.writeEndElement()
|
||||
xml.writeCharacters("\n")
|
||||
|
||||
val instances = irProgram.st.allStructInstances()
|
||||
for (instance in instances) {
|
||||
if(instance.values.isEmpty())
|
||||
TODO("write to IR: BSS struct instance: ${instance.name}")
|
||||
else
|
||||
TODO("write to IR: static struct instance with values: ${instance.name} ${instance.values}")
|
||||
}
|
||||
|
||||
|
||||
xml.writeStartElement("VARIABLESWITHINIT")
|
||||
xml.writeCharacters("\n")
|
||||
val (initNotAligned, initAligned) = variablesWithInit.partition { it.align==0 || it.align==1 }
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package prog8.intermediate
|
||||
|
||||
import prog8.code.*
|
||||
import prog8.code.core.*
|
||||
import prog8.code.INTERNED_STRINGS_MODULENAME
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.Encoding
|
||||
import prog8.code.core.ZeropageWish
|
||||
|
||||
|
||||
// In the Intermediate Representation, all nesting has been removed.
|
||||
@@ -24,6 +26,9 @@ class IRSymbolTable {
|
||||
fun allMemorySlabs(): Sequence<IRStMemorySlab> =
|
||||
table.asSequence().map { it.value }.filterIsInstance<IRStMemorySlab>()
|
||||
|
||||
fun allStructInstances(): Sequence<IRStStructInstance> =
|
||||
table.asSequence().map { it.value }.filterIsInstance<IRStStructInstance>()
|
||||
|
||||
fun lookup(name: String) = table[name]
|
||||
|
||||
fun add(node: IRStNode) {
|
||||
@@ -57,7 +62,8 @@ enum class IRStNodeType {
|
||||
STATICVAR,
|
||||
MEMVAR,
|
||||
MEMORYSLAB,
|
||||
CONST
|
||||
CONST,
|
||||
STRUCTINSTANCE
|
||||
}
|
||||
|
||||
open class IRStNode(val name: String, val type: IRStNodeType)
|
||||
@@ -115,5 +121,11 @@ class IRStArrayElement(val bool: Boolean?, val number: Double?, val addressOfSym
|
||||
}
|
||||
}
|
||||
|
||||
class IRStStructInstance(name: String, val values: IRStArray, val size: Int): IRStNode(name, IRStNodeType.STRUCTINSTANCE) {
|
||||
init {
|
||||
require(size > 0)
|
||||
}
|
||||
}
|
||||
|
||||
typealias IRStArray = List<IRStArrayElement>
|
||||
typealias IRStString = Pair<String, Encoding>
|
||||
|
||||
@@ -91,6 +91,7 @@ class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GL
|
||||
is StMemVar -> node.length
|
||||
is StMemorySlab -> node.size.toInt()
|
||||
is StStaticVariable -> node.length
|
||||
is StStructInstance -> node.size.toInt()
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
@@ -109,7 +110,8 @@ enum class StNodeType {
|
||||
CONSTANT,
|
||||
BUILTINFUNC,
|
||||
MEMORYSLAB,
|
||||
STRUCT
|
||||
STRUCT,
|
||||
STRUCTINSTANCE
|
||||
}
|
||||
|
||||
|
||||
@@ -251,6 +253,7 @@ class StMemVar(name: String,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class StStruct(
|
||||
name: String,
|
||||
val fields: List<Pair<DataType, String>>,
|
||||
@@ -280,6 +283,10 @@ class StMemorySlab(
|
||||
StNode(name, StNodeType.MEMORYSLAB, astNode)
|
||||
|
||||
|
||||
class StStructInstance(name: String, val size: UInt, val initialValues: StArray, astNode: PtNode?) :
|
||||
StNode(name, StNodeType.STRUCTINSTANCE, astNode)
|
||||
|
||||
|
||||
class StSub(name: String, val parameters: List<StSubroutineParameter>, val returns: List<DataType>, astNode: PtNode) :
|
||||
StNode(name, StNodeType.SUBROUTINE, astNode)
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
||||
StSub(node.name, params, node.signature.returns, node)
|
||||
}
|
||||
is PtStructDecl -> {
|
||||
StStruct(node.name, node.members, node)
|
||||
StStruct(node.name, node.fields, node)
|
||||
}
|
||||
is PtVariable -> {
|
||||
val initialNumeric: Double?
|
||||
@@ -120,9 +120,24 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
||||
// don't add memory slabs in nested scope, just put them in the top level of the ST
|
||||
scope.first().add(StMemorySlab("prog8_memoryslab_$slabname", size, align, node))
|
||||
}
|
||||
else if(node.name=="staticalloc") {
|
||||
else if(node.name=="structalloc") {
|
||||
val struct = node.type.subType!!
|
||||
TODO("symboltable alloc and initialize static struct ${struct.scopedNameString}")
|
||||
if(struct is StStruct) {
|
||||
val scopehash = node.parent.hashCode().toUInt().toString(16)
|
||||
val hash = node.position.toString().hashCode().toUInt().toString(16)
|
||||
val structname = struct.scopedNameString.substringAfterLast('.')
|
||||
val label = "prog8_struct_${structname}_${scopehash}_$hash"
|
||||
val size = struct.memsize(program.memsizer).toUInt()
|
||||
val initialValues = node.args.map {
|
||||
when(it) {
|
||||
is PtAddressOf -> StArrayElement(null, it.identifier!!.name, null)
|
||||
is PtBool -> StArrayElement(null, null, it.value)
|
||||
is PtNumber -> StArrayElement(it.number, null, null)
|
||||
else -> throw AssemblyError("invalid structalloc argument type $it")
|
||||
}
|
||||
}
|
||||
scope.first().add(StStructInstance(label, size, initialValues, null))
|
||||
}
|
||||
}
|
||||
null
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
|
||||
is PtArrayIndexer -> "<arrayindexer> ${type(node.type)} ${if(node.splitWords) "[splitwords]" else ""}"
|
||||
is PtBinaryExpression -> "<expr> ${node.operator} ${type(node.type)}"
|
||||
is PtBuiltinFunctionCall -> {
|
||||
if(node.name=="staticalloc") {
|
||||
if(node.name=="structalloc") {
|
||||
node.type.subType!!.scopedNameString+"()"
|
||||
} else {
|
||||
val str = if (node.void) "void " else ""
|
||||
@@ -191,7 +191,7 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
|
||||
is PtIfExpression -> "<ifexpr>"
|
||||
is PtJmpTable -> "<jmptable>"
|
||||
is PtStructDecl -> {
|
||||
"struct ${node.name} { " + node.members.joinToString(" ") { "${it.first} ${it.second}" } + " }"
|
||||
"struct ${node.name} { " + node.fields.joinToString(" ") { "${it.first} ${it.second}" } + " }"
|
||||
}
|
||||
is PtPointerDeref -> {
|
||||
val chain = if(node.chain.isEmpty()) "" else "${node.chain}"
|
||||
|
||||
@@ -233,7 +233,7 @@ 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 PtStructDecl(name: String, val fields: List<Pair<DataType, String>>, position: Position) : PtNamedNode(name, position)
|
||||
|
||||
|
||||
class PtWhen(position: Position) : PtNode(position) {
|
||||
|
||||
Reference in New Issue
Block a user