diff --git a/compiler/src/prog8/ast/expressions/AstExpressions.kt b/compiler/src/prog8/ast/expressions/AstExpressions.kt index dd77e6fc9..95a25ec49 100644 --- a/compiler/src/prog8/ast/expressions/AstExpressions.kt +++ b/compiler/src/prog8/ast/expressions/AstExpressions.kt @@ -231,11 +231,7 @@ class ArrayIndexedExpression(val identifier: IdentifierReference, return when (target.datatype) { in NumericDatatypes -> null in StringDatatypes -> DataType.UBYTE - DataType.ARRAY_UB -> DataType.UBYTE - DataType.ARRAY_B -> DataType.BYTE - DataType.ARRAY_UW -> DataType.UWORD - DataType.ARRAY_W -> DataType.WORD - DataType.ARRAY_F -> DataType.FLOAT + in ArrayDatatypes -> ArrayElementTypes[target.datatype] else -> throw FatalAstException("invalid dt") } } diff --git a/compiler/src/prog8/ast/processing/AstChecker.kt b/compiler/src/prog8/ast/processing/AstChecker.kt index 0cf27b3b3..de620df49 100644 --- a/compiler/src/prog8/ast/processing/AstChecker.kt +++ b/compiler/src/prog8/ast/processing/AstChecker.kt @@ -406,11 +406,13 @@ internal class AstChecker(private val program: Program, if(targetDatatype!=null) { val constVal = assignment.value.constValue(program) if(constVal!=null) { - val arrayspec = if(target.identifier!=null) { - val targetVar = program.namespace.lookup(target.identifier.nameInSource, assignment) as? VarDecl - targetVar?.arraysize - } else null - checkValueTypeAndRange(targetDatatype, + val targetVar = + if(target.identifier!=null) + program.namespace.lookup(target.identifier.nameInSource, assignment) as? VarDecl + else + null + val arrayspec = if(target.identifier!=null) targetVar?.arraysize else null + checkValueTypeAndRange(targetDatatype, targetVar?.struct, arrayspec ?: ArrayIndex(LiteralValue.optimalInteger(-1, assignment.position), assignment.position), constVal, program.heap) } else { @@ -424,8 +426,9 @@ internal class AstChecker(private val program: Program, else checkResult.add(ExpressionError("assignment value is invalid or has no proper datatype", assignment.value.position)) } - else - checkAssignmentCompatible(targetDatatype, sourceDatatype, assignment.value, target, assignment.position) + else { + checkAssignmentCompatible(targetDatatype, target, sourceDatatype, assignment.value, assignment.position) + } } } return assignment @@ -521,7 +524,7 @@ internal class AstChecker(private val program: Program, else ArrayIndex(LiteralValue.optimalInteger(-2, decl.position), decl.position) ) - checkValueTypeAndRange(decl.datatype, arraySpec, decl.value as LiteralValue, program.heap) + checkValueTypeAndRange(decl.datatype, decl.struct, arraySpec, decl.value as LiteralValue, program.heap) } else -> { err("var/const declaration needs a compile-time constant initializer value, or range, instead found: ${decl.value!!.javaClass.simpleName}") @@ -664,7 +667,7 @@ internal class AstChecker(private val program: Program, ArrayIndex.forArray(literalValue, program.heap) else ArrayIndex(LiteralValue.optimalInteger(-3, literalValue.position), literalValue.position) - checkValueTypeAndRange(literalValue.type, arrayspec, literalValue, program.heap) + checkValueTypeAndRange(literalValue.type, null, arrayspec, literalValue, program.heap) val lv = super.visit(literalValue) when(lv.type) { @@ -1009,7 +1012,8 @@ internal class AstChecker(private val program: Program, } } - private fun checkValueTypeAndRange(targetDt: DataType, arrayspec: ArrayIndex, value: LiteralValue, heap: HeapValues) : Boolean { + private fun checkValueTypeAndRange(targetDt: DataType, struct: StructDecl?, + arrayspec: ArrayIndex, value: LiteralValue, heap: HeapValues) : Boolean { fun err(msg: String) : Boolean { checkResult.add(ExpressionError(msg, value.position)) return false @@ -1138,9 +1142,20 @@ internal class AstChecker(private val program: Program, return err("invalid float array initialization value ${value.type}, expected $targetDt") } DataType.STRUCT -> { - if(value.type!=DataType.STRUCT) - return err("cannot assign a normal value to a struct") - TODO("check struct, $value ") + if(value.type in ArrayDatatypes) { + if(value.arrayvalue!!.size != struct!!.numberOfElements) + return err("number of values is not the same as the number of members in the struct") + for(elt in value.arrayvalue.zip(struct.statements)) { + val vardecl = elt.second as VarDecl + val valuetype = elt.first.inferType(program)!! + if (!(valuetype isAssignableTo vardecl.datatype)) { + checkResult.add(ExpressionError("invalid struct member init value type $valuetype, expected ${vardecl.datatype}", elt.first.position)) + return false + } + } + return true + } + return false } } return true @@ -1196,9 +1211,9 @@ internal class AstChecker(private val program: Program, } private fun checkAssignmentCompatible(targetDatatype: DataType, + target: AssignTarget, sourceDatatype: DataType, sourceValue: IExpression, - target: AssignTarget, position: Position) : Boolean { if(sourceValue is RangeExpr) @@ -1214,6 +1229,13 @@ internal class AstChecker(private val program: Program, DataType.STR_S -> sourceDatatype== DataType.STR_S DataType.STRUCT -> { // for now we've decided you cannot assign struct by-value. + // but you can however assign an array to it of the correct size + if(sourceDatatype in ArrayDatatypes) { + val identifier = sourceValue as IdentifierReference + val sourceArraySize = identifier.targetVarDecl(program.namespace)!!.arraysize?.size() + val targetstruct = target.identifier!!.targetVarDecl(program.namespace)!!.struct!! + return targetstruct.numberOfElements == sourceArraySize + } // if(sourceDatatype==DataType.STRUCT) { // val sourcename = (sourceValue as IdentifierReference).nameInSource // val vd1 = program.namespace.lookup(sourcename, target) as? VarDecl diff --git a/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt b/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt index 373eff949..80054e9f7 100644 --- a/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt +++ b/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt @@ -223,8 +223,15 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo // a literal value that's not declared as a variable, which refers to something on the heap. // we need to introduce an auto-generated variable for this to be able to refer to the value! // (note: ususally, this has been taken care of already when the var was created) - val variable = VarDecl(VarDeclType.VAR, literalValue.type, false, null, "$autoHeapValuePrefix${literalValue.heapId}", null, literalValue, - isArray = false, hiddenButDoNotRemove = false, position = literalValue.position) + val declaredType = if(literalValue.isArray) ArrayElementTypes.getValue(literalValue.type) else literalValue.type + val variable = VarDecl(VarDeclType.VAR, + declaredType, + false, + null, + "$autoHeapValuePrefix${literalValue.heapId}", + null, + literalValue, + isArray = literalValue.isArray, hiddenButDoNotRemove = true, position = literalValue.position) anonymousVariablesFromHeap[variable.name] = Pair(literalValue, variable) } return super.visit(literalValue) diff --git a/compiler/src/prog8/ast/statements/AstStatements.kt b/compiler/src/prog8/ast/statements/AstStatements.kt index 4f1d84b4d..310f40e19 100644 --- a/compiler/src/prog8/ast/statements/AstStatements.kt +++ b/compiler/src/prog8/ast/statements/AstStatements.kt @@ -6,6 +6,7 @@ import prog8.ast.expressions.* import prog8.ast.processing.IAstModifyingVisitor import prog8.ast.processing.IAstVisitor import prog8.compiler.HeapValues +import prog8.compiler.target.c64.Mflpt5 class BuiltinFunctionStatementPlaceholder(val name: String, override val position: Position) : IStatement { @@ -162,7 +163,7 @@ class VarDecl(val type: VarDeclType, DataType.FLOAT -> DataType.ARRAY_F else -> { datatypeErrors.add(SyntaxError("array can only contain bytes/words/floats", position)) - DataType.UBYTE + DataType.ARRAY_UB } } @@ -740,6 +741,22 @@ class StructDecl(override val name: String, this.statements.forEach { it.linkParents(this) } } + val numberOfElements: Int + get() = this.statements.size + val memorySize: Int + get() = this.statements.map { + val decl = it as VarDecl + when { + decl.datatype in ByteDatatypes -> 8 + decl.datatype in WordDatatypes -> 16 + decl.datatype==DataType.FLOAT -> Mflpt5.MemorySize + decl.datatype in StringDatatypes -> TODO("stringvalue size") + decl.datatype in ArrayDatatypes -> decl.arraysize!!.size()!! + decl.datatype==DataType.STRUCT -> decl.struct!!.memorySize + else -> throw FatalAstException("can't get size for $decl") + } + }.sum() + override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this) } diff --git a/compiler/src/prog8/compiler/AstToSourceCode.kt b/compiler/src/prog8/compiler/AstToSourceCode.kt index eaf60ce10..70a879ed0 100644 --- a/compiler/src/prog8/compiler/AstToSourceCode.kt +++ b/compiler/src/prog8/compiler/AstToSourceCode.kt @@ -103,10 +103,10 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor { } override fun visit(decl: VarDecl) { - if(decl.hiddenButDoNotRemove) { - // skip autogenerated vardecl - return - } +// if(decl.hiddenButDoNotRemove) { +// // skip autogenerated vardecl +// return +// } when(decl.type) { VarDeclType.VAR -> {} @@ -114,6 +114,7 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor { VarDeclType.MEMORY -> output("&") VarDeclType.STRUCT -> output("${decl.struct!!.name} ") } + output(decl.struct?.name ?: "") output(datatypeString(decl.datatype)) if(decl.arraysize!=null) { decl.arraysize!!.index.accept(this) diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index b911e43d3..8467529ce 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -1446,6 +1446,27 @@ internal class Compiler(private val program: Program) { else -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") } } + DataType.STRUCT -> { + // Assume the value is an array. Flatten the struct assignment into memberwise assignments. + val identifier = stmt.target.identifier!! + val identifierName = identifier.nameInSource.single() + val targetVar = identifier.targetVarDecl(program.namespace)!! + val struct = targetVar.struct!! + val sourceVar = (stmt.value as IdentifierReference).targetVarDecl(program.namespace)!! + if(!sourceVar.isArray) + throw CompilerException("can only assign arrays to structs") + val sourceArray = (sourceVar.value as LiteralValue).arrayvalue!! + for(member in struct.statements.zip(sourceArray)) { + val decl = member.first as VarDecl + val value = member.second.constValue(program)!! + val mangled = mangledStructMemberName(identifierName, decl.name) + val idref = IdentifierReference(listOf(mangled), stmt.position) + val assign = Assignment(AssignTarget(null, idref, null, null, stmt.position), null, value, value.position) + assign.linkParents(stmt) + translate(assign) + } + return + } in StringDatatypes -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") in ArrayDatatypes -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt") else -> throw CompilerException("weird/unknown targetdt") @@ -1499,7 +1520,9 @@ internal class Compiler(private val program: Program) { prog.instr(opcode, RuntimeValue(DataType.UWORD, address)) } VarDeclType.CONST -> throw CompilerException("cannot assign to const") - VarDeclType.STRUCT -> TODO("decltype struct") + VarDeclType.STRUCT -> { + TODO("decltype struct $assignTarget") + } } } else throw CompilerException("invalid assignment target type ${target::class}") } diff --git a/compiler/src/prog8/optimizer/ConstantFolding.kt b/compiler/src/prog8/optimizer/ConstantFolding.kt index 553af6478..3fcd99a84 100644 --- a/compiler/src/prog8/optimizer/ConstantFolding.kt +++ b/compiler/src/prog8/optimizer/ConstantFolding.kt @@ -175,6 +175,9 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor { decl.value = LiteralValue(decl.datatype, initHeapId = heapId, position = litval.position) } } + DataType.STRUCT -> { + // leave it alone for structs. + } else -> throw FatalAstException("invalid array vardecl type ${decl.datatype}") } } diff --git a/compiler/src/prog8/vm/astvm/VariablesCreator.kt b/compiler/src/prog8/vm/astvm/VariablesCreator.kt index 04a6f1c1e..ba52ac295 100644 --- a/compiler/src/prog8/vm/astvm/VariablesCreator.kt +++ b/compiler/src/prog8/vm/astvm/VariablesCreator.kt @@ -40,7 +40,6 @@ class VariablesCreator(private val runtimeVariables: RuntimeVariables, private v when (decl.type) { // we can assume the value in the vardecl already has been converted into a constant LiteralValue here. VarDeclType.VAR -> { - println("$decl") val value = RuntimeValue.from(decl.value as LiteralValue, heap) runtimeVariables.define(decl.definingScope(), decl.name, value) } diff --git a/examples/test.p8 b/examples/test.p8 index c7a214ad5..5a06745e4 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -5,38 +5,49 @@ sub start() { uword derp + ubyte[] v = [22,33,44] - Color foreground ; = [0,1,2] @todo init values - Color background - Color cursor + Color foreground + Color foreground2 = [11,22,33] - foreground.red=99 - background.blue=foreground.red - - ;cursor=foreground ; @todo full by-value copy - - c64scr.print_ub(foreground.red) - c64.CHROUT(':') - c64scr.print_ub(foreground.green) - c64.CHROUT(':') - c64scr.print_ub(foreground.blue) - c64.CHROUT('\n') - c64scr.print_ub(background.red) - c64.CHROUT(':') - c64scr.print_ub(background.green) - c64.CHROUT(':') - c64scr.print_ub(background.blue) - c64.CHROUT('\n') - c64scr.print_ub(cursor.red) - c64.CHROUT(':') - c64scr.print_ub(cursor.green) - c64.CHROUT(':') - c64scr.print_ub(cursor.blue) - c64.CHROUT('\n') - - return + foreground.red = 111 + ; foreground2.red = 111 } +; sub test() { +; Color foreground ; = [0,1,2] ;@todo init values +; Color background +; Color cursor +; +; foreground.red=99 +; background.blue=foreground.red +; +; cursor = [1,2,3] ; assign all members at once +; cursor = v +; cursor=foreground ; @todo memberwise assignment +; +; c64scr.print_ub(foreground.red) +; c64.CHROUT(':') +; c64scr.print_ub(foreground.green) +; c64.CHROUT(':') +; c64scr.print_ub(foreground.blue) +; c64.CHROUT('\n') +; c64scr.print_ub(background.red) +; c64.CHROUT(':') +; c64scr.print_ub(background.green) +; c64.CHROUT(':') +; c64scr.print_ub(background.blue) +; c64.CHROUT('\n') +; c64scr.print_ub(cursor.red) +; c64.CHROUT(':') +; c64scr.print_ub(cursor.green) +; c64.CHROUT(':') +; c64scr.print_ub(cursor.blue) +; c64.CHROUT('\n') +; +; return +; } + struct Color { ubyte red ubyte green