get rid of invalid ARRAY_STRUCT data type (arrays of struct instance are not yet supported)

This commit is contained in:
Irmen de Jong
2025-05-14 17:37:32 +02:00
parent 38448e471c
commit 913ab03963
11 changed files with 49 additions and 68 deletions

View File

@@ -15,8 +15,7 @@ enum class BaseDataType {
ARRAY_SPLITW, // pass by reference, split word layout, subtype is the element type (restricted to word types) 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 POINTER, // typed pointer, subtype is whatever type is pointed to
STRUCT_INSTANCE, // the actual instance of a struct (not directly supported in the language yet, but we need its type) STRUCT_INSTANCE, // the actual instance of a struct (not directly supported in the language yet, but we need its type)
ARRAY_POINTER, // array of pointers (uwords), subtype is whatever type each element points to TODO: is this enum required? can sue ARRAY? ARRAY_POINTER, // array of pointers (uwords), subtype is whatever type each element points to
ARRAY_STRUCT, // array of struct instances, subtype is the struct type of each element TODO: is this enum required? can sue ARRAY?
UNDEFINED; UNDEFINED;
@@ -54,11 +53,10 @@ val BaseDataType.isIntegerOrBool get() = this in arrayOf(BaseDataType.UBYTE, Bas
val BaseDataType.isNumeric get() = this == BaseDataType.FLOAT || this.isInteger val BaseDataType.isNumeric get() = this == BaseDataType.FLOAT || this.isInteger
val BaseDataType.isNumericOrBool get() = this == BaseDataType.BOOL || this.isNumeric 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.isSigned get() = this in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
val BaseDataType.isArray get() = this == BaseDataType.ARRAY || this == BaseDataType.ARRAY_SPLITW || this == BaseDataType.ARRAY_POINTER || this == BaseDataType.ARRAY_STRUCT val BaseDataType.isArray get() = this == BaseDataType.ARRAY || this == BaseDataType.ARRAY_SPLITW || this == BaseDataType.ARRAY_POINTER
val BaseDataType.isPointer get() = this == BaseDataType.POINTER val BaseDataType.isPointer get() = this == BaseDataType.POINTER
val BaseDataType.isStructInstance get() = this == BaseDataType.STRUCT_INSTANCE val BaseDataType.isStructInstance get() = this == BaseDataType.STRUCT_INSTANCE
val BaseDataType.isPointerArray get() = this == BaseDataType.ARRAY_POINTER val BaseDataType.isPointerArray get() = this == BaseDataType.ARRAY_POINTER
val BaseDataType.isStructArray get() = this == BaseDataType.ARRAY_STRUCT
val BaseDataType.isSplitWordArray get() = this == BaseDataType.ARRAY_SPLITW val BaseDataType.isSplitWordArray get() = this == BaseDataType.ARRAY_SPLITW
val BaseDataType.isIterable get() = this in arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW, BaseDataType.ARRAY_POINTER) val BaseDataType.isIterable get() = this in arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW, BaseDataType.ARRAY_POINTER)
val BaseDataType.isPassByRef get() = this.isIterable && !this.isPointer val BaseDataType.isPassByRef get() = this.isIterable && !this.isPointer
@@ -77,9 +75,6 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
base.isPointerArray -> { base.isPointerArray -> {
require(sub!=null || subType!=null || subTypeFromAntlr!=null) require(sub!=null || subType!=null || subTypeFromAntlr!=null)
} }
base.isStructArray -> {
require(sub==null && (subType!=null || subTypeFromAntlr!=null))
}
base.isArray -> { base.isArray -> {
require(sub != null && subType==null && subTypeFromAntlr==null) require(sub != null && subType==null && subTypeFromAntlr==null)
if(base.isSplitWordArray) if(base.isSplitWordArray)
@@ -149,12 +144,8 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
fun arrayOfPointersTo(sub: BaseDataType?, subType: ISubType?): DataType = fun arrayOfPointersTo(sub: BaseDataType?, subType: ISubType?): DataType =
DataType(BaseDataType.ARRAY_POINTER, sub, subType) DataType(BaseDataType.ARRAY_POINTER, sub, subType)
fun arrayOfStructs(subType: ISubType?): DataType =
DataType(BaseDataType.ARRAY_STRUCT, null, subType)
fun arrayOfPointersFromAntlrTo(sub: BaseDataType?, identifier: List<String>?): DataType = fun arrayOfPointersFromAntlrTo(sub: BaseDataType?, identifier: List<String>?): DataType =
DataType(BaseDataType.ARRAY_POINTER, sub, null, identifier) DataType(BaseDataType.ARRAY_POINTER, sub, null, identifier)
fun arrayOfStructsFromAntlr(identifier: List<String>): DataType =
DataType(BaseDataType.ARRAY_STRUCT, null, null, identifier)
fun pointer(base: BaseDataType): DataType = DataType(BaseDataType.POINTER, base, null) fun pointer(base: BaseDataType): DataType = DataType(BaseDataType.POINTER, base, null)
fun pointerToType(type: ISubType): DataType = DataType(BaseDataType.POINTER, null, type) fun pointerToType(type: ISubType): DataType = DataType(BaseDataType.POINTER, null, type)
@@ -172,22 +163,20 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
fun elementType(): DataType = fun elementType(): DataType =
when { when {
isPointerArray -> DataType(BaseDataType.POINTER, sub, subType) isPointerArray -> DataType(BaseDataType.POINTER, sub, subType)
isStructArray -> DataType(BaseDataType.STRUCT_INSTANCE, sub, subType)
base.isArray || base==BaseDataType.STR -> forDt(sub!!) base.isArray || base==BaseDataType.STR -> forDt(sub!!)
else -> throw IllegalArgumentException("not an array") else -> throw IllegalArgumentException("not an array")
} }
fun typeForAddressOf(msb: Boolean): DataType { fun typeForAddressOf(msb: Boolean): DataType {
// TODO implement typed address-of.
// TODO no typed pointer possible yet that points to an array
if (isUndefined) if (isUndefined)
return if(msb) pointer(BaseDataType.UBYTE) else UWORD return if(msb) pointer(BaseDataType.UBYTE) else UWORD
else { else {
// TODO implement these as well:
// if(isBasic) // if(isBasic)
// return pointer(base) // return pointer(base) // TODO breaks 6502 codegen atm
// if(isString) // if(isString) // TODO pointer to string == ptr to ubyte? breaks 6502 codegen atm.
// return pointer(BaseDataType.UBYTE) // return pointer(BaseDataType.UBYTE)
// if(isArray) // TODO pointer to array == ptr to element type? breaks 6502 codegen atm.
// return pointer(elementType().base)
if (subType != null) if (subType != null)
return pointerToType(subType!!) return pointerToType(subType!!)
else if (isArray) { else if (isArray) {
@@ -224,9 +213,6 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
BaseDataType.ARRAY_POINTER -> { BaseDataType.ARRAY_POINTER -> {
if(sub!=null) "^^${sub.name.lowercase()}[] (split)" else if (subType!=null) "^^${subType!!.scopedNameString}[] (split)" else "^^${subTypeFromAntlr}[] (split)" if(sub!=null) "^^${sub.name.lowercase()}[] (split)" else if (subType!=null) "^^${subType!!.scopedNameString}[] (split)" else "^^${subTypeFromAntlr}[] (split)"
} }
BaseDataType.ARRAY_STRUCT -> {
if(sub!=null) "${sub.name.lowercase()}[]" else if (subType!=null) "${subType!!.scopedNameString}[]" else "${subTypeFromAntlr}[]"
}
BaseDataType.STRUCT_INSTANCE -> { BaseDataType.STRUCT_INSTANCE -> {
sub?.name?.lowercase() ?: if (subType!=null) subType!!.scopedNameString else "$subTypeFromAntlr" sub?.name?.lowercase() ?: if (subType!=null) subType!!.scopedNameString else "$subTypeFromAntlr"
} }
@@ -266,14 +252,6 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
else -> "????? [" else -> "????? ["
} }
} }
BaseDataType.ARRAY_STRUCT -> {
when {
sub!=null -> "${sub.name.lowercase()}["
subType!=null -> "${subType!!.scopedNameString}["
subTypeFromAntlr!=null -> "${subTypeFromAntlr!!.joinToString(".")}["
else -> "????? ["
}
}
BaseDataType.ARRAY -> { BaseDataType.ARRAY -> {
when(sub) { when(sub) {
BaseDataType.UBYTE -> "ubyte[" BaseDataType.UBYTE -> "ubyte["
@@ -316,7 +294,6 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
} }
BaseDataType.STRUCT_INSTANCE -> false // we cannot deal with actual struct instances yet in any shape or form (only getting fields from it) BaseDataType.STRUCT_INSTANCE -> false // we cannot deal with actual struct instances yet in any shape or form (only getting fields from it)
BaseDataType.ARRAY_POINTER -> TODO("check assignability of array of pointers") BaseDataType.ARRAY_POINTER -> TODO("check assignability of array of pointers")
BaseDataType.ARRAY_STRUCT -> false // never assign an array of struct instances to somewhere else
BaseDataType.UNDEFINED -> false BaseDataType.UNDEFINED -> false
} }
@@ -350,7 +327,6 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
val isPointer = base.isPointer val isPointer = base.isPointer
val isStructInstance = base.isStructInstance val isStructInstance = base.isStructInstance
val isPointerArray = base.isPointerArray val isPointerArray = base.isPointerArray
val isStructArray = base.isStructArray
val isBoolArray = base.isArray && sub == BaseDataType.BOOL val isBoolArray = base.isArray && sub == BaseDataType.BOOL
val isByteArray = base.isArray && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE) val isByteArray = base.isArray && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE)
val isUnsignedByteArray = base.isArray && sub == BaseDataType.UBYTE val isUnsignedByteArray = base.isArray && sub == BaseDataType.UBYTE

View File

@@ -767,9 +767,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
BaseDataType.ARRAY_POINTER -> { BaseDataType.ARRAY_POINTER -> {
TODO("typecast to array of pointers $valueDt -> ${cast.type}") TODO("typecast to array of pointers $valueDt -> ${cast.type}")
} }
BaseDataType.ARRAY_STRUCT -> {
TODO("typecast to array of structs $valueDt -> ${cast.type}")
}
else -> throw AssemblyError("weird cast type") else -> throw AssemblyError("weird cast type")
} }

View File

@@ -1029,6 +1029,9 @@ internal class AstChecker(private val program: Program,
errors.err("pointer arrays can only be @split", decl.position) errors.err("pointer arrays can only be @split", decl.position)
} }
if(decl.datatype.isStructInstance) {
errors.err("struct instances cannot be declared directly, use pointer and allocation call instead", decl.position)
}
if (decl.dirty) { if (decl.dirty) {
if(decl.datatype.isString) if(decl.datatype.isString)
@@ -1405,10 +1408,10 @@ internal class AstChecker(private val program: Program,
} }
} }
} }
else if(expr.operator !in emptySet<String>()) { // TODO add + and - operators support else if(expr.operator !in emptyArray<String>()) { // TODO add + and - operators support for pointer arithmetic
if (leftDt.isPointer || leftDt.isPointerArray || rightDt.isPointer || rightDt.isPointerArray) { if (leftDt.isPointer || leftDt.isPointerArray || rightDt.isPointer || rightDt.isPointerArray) {
errors.err("pointer arithmetic only supported for + and - operators (but these are currently not yet supported for this target, will be fixed later)", expr.right.position) errors.err("pointer arithmetic not yet supported for this target (will be fixed later)", expr.right.position)
// TODO final error should be: errors.err("pointer arithmetic only supported for + and - operators", expr.position) // TODO final error should be different: pointer arithmetic only supported for + and - operators
} }
} }

View File

@@ -64,10 +64,11 @@ main {
val errors = ErrorReporterForTests() val errors = ErrorReporterForTests()
compileText(VMTarget(), false, src, outputDir, errors=errors) compileText(VMTarget(), false, src, outputDir, errors=errors)
val err = errors.errors val err = errors.errors
err.size shouldBe 3 err.size shouldBe 4
err[0] shouldContain "uword doesn't match" err[0] shouldContain "struct instances cannot be declared"
err[1] shouldContain "structs can only be passed via a pointer" err[1] shouldContain "uword doesn't match"
err[2] shouldContain "structs can only be returned via a pointer" err[2] shouldContain "structs can only be passed via a pointer"
err[3] shouldContain "structs can only be returned via a pointer"
} }
test("pointers in subroutine return values") { test("pointers in subroutine return values") {

View File

@@ -876,7 +876,7 @@ private fun VardeclContext.toAst(type: VarDeclType, value: Expression?): VarDecl
if(baseDt.isPointer) if(baseDt.isPointer)
DataType.arrayOfPointersFromAntlrTo(baseDt.sub, baseDt.subTypeFromAntlr) DataType.arrayOfPointersFromAntlrTo(baseDt.sub, baseDt.subTypeFromAntlr)
else if(baseDt.isStructInstance) else if(baseDt.isStructInstance)
DataType.arrayOfStructsFromAntlr(baseDt.subTypeFromAntlr!!) throw SyntaxError("array of structures not allowed (use array of pointers)", toPosition())
else else
DataType.arrayFor(baseDt.base, split!=SplitWish.NOSPLIT) DataType.arrayFor(baseDt.base, split!=SplitWish.NOSPLIT)
} }

View File

@@ -38,24 +38,23 @@ STRUCTS and TYPED POINTERS
- DONE: support comparison operators on pointers - DONE: support comparison operators on pointers
- DONE: implement augmented assignment on pointer dereference - DONE: implement augmented assignment on pointer dereference
- DONE: pointer types in subroutine signatures (both normal and asm-subs, parameters and return values) - DONE: pointer types in subroutine signatures (both normal and asm-subs, parameters and return values)
- DONE: arrays of structs? No -> Just an array of uword pointers to said structs.
- DONE: what about pointers to subroutines? should these be typed as well now? Probably not, just stick with UWORD untyped pointer to avoid needless complexity.
- fix actual _msb/_lsb storage of the split-words pointer-arrays - fix actual _msb/_lsb storage of the split-words pointer-arrays
- support chaining pointer dereference on function calls that return a pointer. (type checking now fails on stuff like func().field and func().next.field)
- make typeForAddressOf() be even more specific about the typed pointers it returns for the address-of operator. + unit test.
- implement inplace logical and & or, with short-cirtuit, on dereferenced pointer (see TODO "or= on pointer dereference, with short-circuit") - implement inplace logical and & or, with short-cirtuit, on dereferenced pointer (see TODO "or= on pointer dereference, with short-circuit")
- are the ARRAY_POINTER and ARRAY_STRUCT data type enums realy needed? can't we just use ARRAY?
- 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.
- allow memory-mapped structs? Something like &Sprite sprite0 = $9000 basically behaves identically to a typed pointer, but the address is immutable as usual
- STR should become asssignment compatible with ubyte^^ but local scoped STR should still be accessed directly using LDA str,Y instead of through the pointer - STR should become asssignment compatible with ubyte^^ but local scoped STR should still be accessed directly using LDA str,Y instead of through the pointer
- make typeForAddressOf() be even more specific about the typed pointers it returns for the address-of operator. + unit test. Needs fixes in 6502 codegen too though...
- 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"
- (later, nasty parser problem:) support chaining pointer dereference on function calls that return a pointer. (type checking now fails on stuff like func().field and func().next.field)
- allow memory-mapped structs? Something like &Sprite sprite0 = $9000 basically behaves identically to a typed pointer, but the address is immutable as usual
- existing ARRAY remain unchanged (don't become typed pointers) so we can keep doing register-indexed addressing directly on them. - existing 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..?) - rather than str or uword parameter types for routines with a string argument, use ^^str (or ^^ubyte maybe? these are more or less identical..?)
- pointer-to-array syntax = TBD - pointer-to-array syntax = TBD
- pointer-to-string syntax = TBD - pointer-to-string syntax = TBD
- what about pointers to subroutines? should these be typed as well now? - add more unit tests for all changes (pointers and structs)
- What about static initialization of an array of struct pointers? -> impossible right now because the pointer values are not contants
- 6502 codegen should warn about writing to initialized struct instances when using romable code, like with arrays "can only be used as read-only in ROMable code" - 6502 codegen should warn about writing to initialized struct instances when using romable code, like with arrays "can only be used as read-only in ROMable code"
- 6502 asm symbol name prefixing should work for dereferences too. - 6502 asm symbol name prefixing should work for dereferences too.
- What about static initialization of an array of struct pointers? -> impossible right now because the pointer values are not constants
- update syntax highlighting files - update syntax highlighting files

View File

@@ -1,3 +1,5 @@
%import textio
main { main {
struct MNode { struct MNode {
bool flag bool flag
@@ -10,6 +12,25 @@ main {
} }
sub start() { sub start() {
^^MNode[5] @shared nodes
nodes[0] = MNode()
nodes[1] = MNode()
nodes[2] = MNode()
nodes[3] = MNode()
nodes[4] = MNode()
txt.print_uw(nodes[0])
txt.spc()
txt.print_uw(nodes[1])
txt.spc()
txt.print_uw(nodes[2])
txt.spc()
txt.print_uw(nodes[3])
txt.spc()
txt.print_uw(nodes[4])
txt.nl()
^^MNode mn1 = MNode() ^^MNode mn1 = MNode()
mn1 = func(mn1) mn1 = func(mn1)

View File

@@ -636,10 +636,7 @@ class IRFileReader {
"float" -> DataType.arrayFor(BaseDataType.FLOAT, false) "float" -> DataType.arrayFor(BaseDataType.FLOAT, false)
"long" -> DataType.arrayFor(BaseDataType.LONG, false) "long" -> DataType.arrayFor(BaseDataType.LONG, false)
else -> { else -> {
if('.' in type) throw IRParseException("invalid dt $type")
DataType.arrayOfStructs(IRSubtypePlaceholder(type))
else
throw IRParseException("invalid dt $type")
} }
} }
} else { } else {

View File

@@ -35,12 +35,6 @@ fun DataType.irTypeString(length: UInt?): String {
else else
"^${subType!!.scopedNameString}[$lengthStr]" "^${subType!!.scopedNameString}[$lengthStr]"
} }
BaseDataType.ARRAY_STRUCT -> {
if(sub!=null)
"${sub!!.name.lowercase()}[$lengthStr]"
else
"${subType!!.scopedNameString}[$lengthStr]"
}
BaseDataType.ARRAY -> { BaseDataType.ARRAY -> {
when(this.sub) { when(this.sub) {
BaseDataType.UBYTE -> "ubyte[$lengthStr]" BaseDataType.UBYTE -> "ubyte[$lengthStr]"

View File

@@ -154,9 +154,6 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
} }
else throw IllegalArgumentException("invalid array dt") else throw IllegalArgumentException("invalid array dt")
} }
node.type.isStructArray -> {
TODO("unsized array of structs")
}
node.type.isArray -> { node.type.isArray -> {
val eltType = node.type.elementType() val eltType = node.type.elementType()
"${eltType}[] $split $align ${node.name}" "${eltType}[] $split $align ${node.name}"

View File

@@ -24,11 +24,7 @@ internal class VmVariableAllocator(st: IRSymbolTable, val encoding: IStringEncod
variable.dt.isString -> variable.onetimeInitializationStringValue!!.first.length + 1 // include the zero byte variable.dt.isString -> variable.onetimeInitializationStringValue!!.first.length + 1 // include the zero byte
variable.dt.isNumericOrBool -> memsizer.memorySize(variable.dt, null) variable.dt.isNumericOrBool -> memsizer.memorySize(variable.dt, null)
variable.dt.isArray -> memsizer.memorySize(variable.dt, variable.length!!.toInt()) variable.dt.isArray -> memsizer.memorySize(variable.dt, variable.length!!.toInt())
variable.dt.isStructInstance -> { variable.dt.isStructInstance -> throw InternalCompilerException("struct instances cannot be directly declared")
TODO("struct instances") // needed or not???
// var name = variable.dt.subType!!.scopedNameString
// var target = st.lookup(name) // TODO should yield the struct definition, but St doesn't contain those yet?
}
else -> throw InternalCompilerException("weird dt") else -> throw InternalCompilerException("weird dt")
} }