diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index 3d02a3a15..0ed2cbb36 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -629,6 +629,10 @@ internal class AstChecker(private val program: Program, } } + if(fcall?.target?.targetStructDecl()!=null) + // Struct(...) initializer + return super.visit(assignment) + // unfortunately the AST regarding pointer dereferencing is a bit of a mess, and we cannot do precise type checking on elements inside such expressions yet. if(assignment.value.inferType(program).isUnknown) { val binexpr = assignment.value as? BinaryExpression @@ -2213,7 +2217,7 @@ internal class AstChecker(private val program: Program, if(sourceDatatype.isPointer) { if(!(sourceDatatype isAssignableTo targetDatatype)) errors.err("cannot assign different pointer type", position) - } else if(!sourceDatatype.isUnsignedWord) + } else if(!sourceDatatype.isUnsignedWord && !sourceDatatype.isStructInstance) errors.err("can only assign uword or correct pointer type to a pointer", position) } else if(targetDatatype.isString && sourceDatatype.isUnsignedWord) diff --git a/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt b/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt index 926a43e60..c9957cc41 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstExtensions.kt @@ -227,7 +227,7 @@ internal fun IdentifierReference.checkFunctionOrLabelExists(program: Program, st else errors.err("cannot call that: ${this.nameInSource.joinToString(".")}", this.position) } - is Alias -> { + is Alias, is StructDecl -> { return targetStatement } null -> { diff --git a/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt index a624977e5..15869e0a3 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt @@ -252,7 +252,7 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter, if(target.type!=VarDeclType.VAR || !target.datatype.isUnsignedWord) errors.err("wrong address variable datatype, expected uword", call.target.position) } - is Alias -> {} + is Alias, is StructDecl -> {} null -> {} else -> errors.err("cannot call this as a subroutine or function", call.target.position) } diff --git a/compiler/src/prog8/compiler/astprocessing/SimplifiedAstMaker.kt b/compiler/src/prog8/compiler/astprocessing/SimplifiedAstMaker.kt index 776be5702..e85fdaed1 100644 --- a/compiler/src/prog8/compiler/astprocessing/SimplifiedAstMaker.kt +++ b/compiler/src/prog8/compiler/astprocessing/SimplifiedAstMaker.kt @@ -366,7 +366,17 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro val (target, _) = srcCall.target.targetNameAndType(program) val iType = srcCall.inferType(program) - val call = PtFunctionCall(target, iType.isUnknown && srcCall.parent !is Assignment, iType.getOrElse { DataType.UNDEFINED }, srcCall.position) + val call = + if(iType.isStructInstance) { + // 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) + } else { + // regular function call + PtFunctionCall(target, iType.isUnknown && srcCall.parent !is Assignment, iType.getOrElse { DataType.UNDEFINED }, srcCall.position) + } + for (arg in srcCall.args) call.add(transformExpression(arg)) return call diff --git a/compiler/src/prog8/compiler/astprocessing/SimplifiedAstPostprocess.kt b/compiler/src/prog8/compiler/astprocessing/SimplifiedAstPostprocess.kt index 5e6c4bef2..56f0f60e9 100644 --- a/compiler/src/prog8/compiler/astprocessing/SimplifiedAstPostprocess.kt +++ b/compiler/src/prog8/compiler/astprocessing/SimplifiedAstPostprocess.kt @@ -9,11 +9,11 @@ import prog8.code.core.* internal fun postprocessSimplifiedAst(program: PtProgram, st: SymbolTable, errors: IErrorReporter) { processDefers(program, st, errors) - processSubtypes(program, st) + processSubtypesIntoStReferences(program, st) } -private fun processSubtypes(program: PtProgram, st: SymbolTable) { +private fun processSubtypesIntoStReferences(program: PtProgram, st: SymbolTable) { fun getStStruct(subType: ISubType): StStruct { val stNode = st.lookup(subType.scopedNameString) as? StStruct diff --git a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt index 69c8c28eb..87191f322 100644 --- a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt +++ b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt @@ -1433,6 +1433,9 @@ class FunctionCallExpression(override var target: IdentifierReference, return InferredTypes.unknown() // has multiple return types... so not a single resulting datatype possible } + is StructDecl -> { + return InferredTypes.knownFor(DataType.structInstance(stmt)) + } else -> return InferredTypes.unknown() } } diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 61dbc82ee..b635e54ed 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -28,14 +28,16 @@ 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 -- is the ARRAY_POINTER data type enum realy needed? can just use ARRAY? +- are the ARRAY_POINTER and ARRAY_STRUCT data type enums realy needed? can just use ARRAY? - 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) - 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 = TBD +- 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..?) diff --git a/examples/test.p8 b/examples/test.p8 index bc576dbe5..064e61399 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -16,10 +16,10 @@ main { ^^Node next } - Node @shared node1,node2,node3 - node1.value = 42 - node1.value2 = 55 - node1.value3 = 66 + ; 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 n0,n1,n2,n3,n4,n5,n6,n7,n8 diff --git a/simpleAst/src/prog8/code/SymbolTable.kt b/simpleAst/src/prog8/code/SymbolTable.kt index 852c4981f..862f32107 100644 --- a/simpleAst/src/prog8/code/SymbolTable.kt +++ b/simpleAst/src/prog8/code/SymbolTable.kt @@ -253,15 +253,15 @@ class StMemVar(name: String, class StStruct( name: String, - val members: List>, + val fields: List>, astNode: PtStructDecl? ) : StNode(name, StNodeType.STRUCT, astNode), ISubType { - fun memsize(sizer: IMemSizer): Int = members.sumOf { sizer.memorySize(it.first, 1) } + fun memsize(sizer: IMemSizer): Int = fields.sumOf { sizer.memorySize(it.first, 1) } fun getField(name: String, sizer: IMemSizer): Pair { // returns type and byte offset of the given field var offset = 0 - for((dt, definedname) in members) { + for((dt, definedname) in fields) { if(name==definedname) return dt to offset offset += sizer.memorySize(dt, null) diff --git a/simpleAst/src/prog8/code/SymbolTableMaker.kt b/simpleAst/src/prog8/code/SymbolTableMaker.kt index 45326bdf1..f4a023696 100644 --- a/simpleAst/src/prog8/code/SymbolTableMaker.kt +++ b/simpleAst/src/prog8/code/SymbolTableMaker.kt @@ -120,6 +120,10 @@ 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") { + val struct = node.type.subType!! + TODO("symboltable alloc and initialize static struct ${struct.scopedNameString}") + } null } else -> null // node is not present in the ST diff --git a/simpleAst/src/prog8/code/ast/AstPrinter.kt b/simpleAst/src/prog8/code/ast/AstPrinter.kt index 3013b57f3..ce987c5f3 100644 --- a/simpleAst/src/prog8/code/ast/AstPrinter.kt +++ b/simpleAst/src/prog8/code/ast/AstPrinter.kt @@ -46,8 +46,12 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni is PtArrayIndexer -> " ${type(node.type)} ${if(node.splitWords) "[splitwords]" else ""}" is PtBinaryExpression -> " ${node.operator} ${type(node.type)}" is PtBuiltinFunctionCall -> { - val str = if(node.void) "void " else "" - str + node.name + "()" + if(node.name=="staticalloc") { + node.type.subType!!.scopedNameString+"()" + } else { + val str = if (node.void) "void " else "" + str + node.name + "()" + } } is PtContainmentCheck -> "in" is PtFunctionCall -> {