mirror of
https://github.com/irmen/prog8.git
synced 2025-11-02 13:16:07 +00:00
get rid of invalid ARRAY_STRUCT data type (arrays of struct instance are not yet supported)
This commit is contained in:
@@ -15,8 +15,7 @@ enum class BaseDataType {
|
||||
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
|
||||
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_STRUCT, // array of struct instances, subtype is the struct type of each element TODO: is this enum required? can sue ARRAY?
|
||||
ARRAY_POINTER, // array of pointers (uwords), subtype is whatever type each element points to
|
||||
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.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 || 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.isStructInstance get() = this == BaseDataType.STRUCT_INSTANCE
|
||||
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.isIterable get() = this in arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW, BaseDataType.ARRAY_POINTER)
|
||||
val BaseDataType.isPassByRef get() = this.isIterable && !this.isPointer
|
||||
@@ -77,9 +75,6 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
base.isPointerArray -> {
|
||||
require(sub!=null || subType!=null || subTypeFromAntlr!=null)
|
||||
}
|
||||
base.isStructArray -> {
|
||||
require(sub==null && (subType!=null || subTypeFromAntlr!=null))
|
||||
}
|
||||
base.isArray -> {
|
||||
require(sub != null && subType==null && subTypeFromAntlr==null)
|
||||
if(base.isSplitWordArray)
|
||||
@@ -149,12 +144,8 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
|
||||
fun arrayOfPointersTo(sub: BaseDataType?, subType: ISubType?): DataType =
|
||||
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 =
|
||||
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 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 =
|
||||
when {
|
||||
isPointerArray -> DataType(BaseDataType.POINTER, sub, subType)
|
||||
isStructArray -> DataType(BaseDataType.STRUCT_INSTANCE, sub, subType)
|
||||
base.isArray || base==BaseDataType.STR -> forDt(sub!!)
|
||||
else -> throw IllegalArgumentException("not an array")
|
||||
}
|
||||
|
||||
fun typeForAddressOf(msb: Boolean): DataType {
|
||||
// TODO implement typed address-of.
|
||||
// TODO no typed pointer possible yet that points to an array
|
||||
if (isUndefined)
|
||||
return if(msb) pointer(BaseDataType.UBYTE) else UWORD
|
||||
else {
|
||||
// TODO implement these as well:
|
||||
// if(isBasic)
|
||||
// return pointer(base)
|
||||
// if(isString)
|
||||
// return pointer(base) // TODO breaks 6502 codegen atm
|
||||
// if(isString) // TODO pointer to string == ptr to ubyte? breaks 6502 codegen atm.
|
||||
// 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)
|
||||
return pointerToType(subType!!)
|
||||
else if (isArray) {
|
||||
@@ -224,9 +213,6 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
BaseDataType.ARRAY_POINTER -> {
|
||||
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 -> {
|
||||
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 -> "????? ["
|
||||
}
|
||||
}
|
||||
BaseDataType.ARRAY_STRUCT -> {
|
||||
when {
|
||||
sub!=null -> "${sub.name.lowercase()}["
|
||||
subType!=null -> "${subType!!.scopedNameString}["
|
||||
subTypeFromAntlr!=null -> "${subTypeFromAntlr!!.joinToString(".")}["
|
||||
else -> "????? ["
|
||||
}
|
||||
}
|
||||
BaseDataType.ARRAY -> {
|
||||
when(sub) {
|
||||
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.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
|
||||
}
|
||||
|
||||
@@ -350,7 +327,6 @@ class DataType private constructor(val base: BaseDataType, val sub: BaseDataType
|
||||
val isPointer = base.isPointer
|
||||
val isStructInstance = base.isStructInstance
|
||||
val isPointerArray = base.isPointerArray
|
||||
val isStructArray = base.isStructArray
|
||||
val isBoolArray = base.isArray && sub == BaseDataType.BOOL
|
||||
val isByteArray = base.isArray && (sub == BaseDataType.UBYTE || sub == BaseDataType.BYTE)
|
||||
val isUnsignedByteArray = base.isArray && sub == BaseDataType.UBYTE
|
||||
|
||||
@@ -767,9 +767,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
BaseDataType.ARRAY_POINTER -> {
|
||||
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")
|
||||
}
|
||||
|
||||
|
||||
@@ -1029,6 +1029,9 @@ internal class AstChecker(private val program: Program,
|
||||
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.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) {
|
||||
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)
|
||||
// TODO final error should be: errors.err("pointer arithmetic only supported for + and - operators", expr.position)
|
||||
errors.err("pointer arithmetic not yet supported for this target (will be fixed later)", expr.right.position)
|
||||
// TODO final error should be different: pointer arithmetic only supported for + and - operators
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,10 +64,11 @@ main {
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(VMTarget(), false, src, outputDir, errors=errors)
|
||||
val err = errors.errors
|
||||
err.size shouldBe 3
|
||||
err[0] shouldContain "uword doesn't match"
|
||||
err[1] shouldContain "structs can only be passed via a pointer"
|
||||
err[2] shouldContain "structs can only be returned via a pointer"
|
||||
err.size shouldBe 4
|
||||
err[0] shouldContain "struct instances cannot be declared"
|
||||
err[1] shouldContain "uword doesn't match"
|
||||
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") {
|
||||
|
||||
@@ -876,7 +876,7 @@ private fun VardeclContext.toAst(type: VarDeclType, value: Expression?): VarDecl
|
||||
if(baseDt.isPointer)
|
||||
DataType.arrayOfPointersFromAntlrTo(baseDt.sub, baseDt.subTypeFromAntlr)
|
||||
else if(baseDt.isStructInstance)
|
||||
DataType.arrayOfStructsFromAntlr(baseDt.subTypeFromAntlr!!)
|
||||
throw SyntaxError("array of structures not allowed (use array of pointers)", toPosition())
|
||||
else
|
||||
DataType.arrayFor(baseDt.base, split!=SplitWish.NOSPLIT)
|
||||
}
|
||||
|
||||
@@ -38,24 +38,23 @@ STRUCTS and TYPED POINTERS
|
||||
- DONE: support comparison operators on pointers
|
||||
- DONE: implement augmented assignment on pointer dereference
|
||||
- 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
|
||||
- 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")
|
||||
- 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
|
||||
- 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.
|
||||
- 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-string syntax = TBD
|
||||
- what about pointers to subroutines? should these be typed as well now?
|
||||
- What about static initialization of an array of struct pointers? -> impossible right now because the pointer values are not contants
|
||||
- add more unit tests for all changes (pointers and structs)
|
||||
- 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.
|
||||
- What about static initialization of an array of struct pointers? -> impossible right now because the pointer values are not constants
|
||||
- update syntax highlighting files
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
%import textio
|
||||
|
||||
main {
|
||||
struct MNode {
|
||||
bool flag
|
||||
@@ -10,6 +12,25 @@ main {
|
||||
}
|
||||
|
||||
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()
|
||||
mn1 = func(mn1)
|
||||
|
||||
|
||||
@@ -636,10 +636,7 @@ class IRFileReader {
|
||||
"float" -> DataType.arrayFor(BaseDataType.FLOAT, false)
|
||||
"long" -> DataType.arrayFor(BaseDataType.LONG, false)
|
||||
else -> {
|
||||
if('.' in type)
|
||||
DataType.arrayOfStructs(IRSubtypePlaceholder(type))
|
||||
else
|
||||
throw IRParseException("invalid dt $type")
|
||||
throw IRParseException("invalid dt $type")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -35,12 +35,6 @@ fun DataType.irTypeString(length: UInt?): String {
|
||||
else
|
||||
"^${subType!!.scopedNameString}[$lengthStr]"
|
||||
}
|
||||
BaseDataType.ARRAY_STRUCT -> {
|
||||
if(sub!=null)
|
||||
"${sub!!.name.lowercase()}[$lengthStr]"
|
||||
else
|
||||
"${subType!!.scopedNameString}[$lengthStr]"
|
||||
}
|
||||
BaseDataType.ARRAY -> {
|
||||
when(this.sub) {
|
||||
BaseDataType.UBYTE -> "ubyte[$lengthStr]"
|
||||
|
||||
@@ -154,9 +154,6 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
|
||||
}
|
||||
else throw IllegalArgumentException("invalid array dt")
|
||||
}
|
||||
node.type.isStructArray -> {
|
||||
TODO("unsized array of structs")
|
||||
}
|
||||
node.type.isArray -> {
|
||||
val eltType = node.type.elementType()
|
||||
"${eltType}[] $split $align ${node.name}"
|
||||
|
||||
@@ -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.isNumericOrBool -> memsizer.memorySize(variable.dt, null)
|
||||
variable.dt.isArray -> memsizer.memorySize(variable.dt, variable.length!!.toInt())
|
||||
variable.dt.isStructInstance -> {
|
||||
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?
|
||||
}
|
||||
variable.dt.isStructInstance -> throw InternalCompilerException("struct instances cannot be directly declared")
|
||||
else -> throw InternalCompilerException("weird dt")
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user