From 61af72b9060ee8106776d62323811bdace40d5a6 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 16 Jul 2019 00:08:28 +0200 Subject: [PATCH] struct literals --- compiler/src/prog8/ast/Interfaces.kt | 4 + compiler/src/prog8/ast/antlr/Antr2Kotlin.kt | 7 + .../prog8/ast/expressions/AstExpressions.kt | 26 ++- .../src/prog8/ast/processing/AstChecker.kt | 40 ++-- .../ast/processing/IAstModifyingVisitor.kt | 5 + .../src/prog8/ast/processing/IAstVisitor.kt | 4 + .../ast/processing/StatementReorderer.kt | 173 ++++++++++-------- .../src/prog8/ast/statements/AstStatements.kt | 4 +- .../src/prog8/compiler/AstToSourceCode.kt | 58 +++--- compiler/src/prog8/compiler/Compiler.kt | 27 ++- compiler/src/prog8/compiler/Main.kt | 3 +- .../src/prog8/functions/BuiltinFunctions.kt | 2 +- examples/test.p8 | 9 +- parser/antlr/prog8.g4 | 6 +- 14 files changed, 237 insertions(+), 131 deletions(-) diff --git a/compiler/src/prog8/ast/Interfaces.kt b/compiler/src/prog8/ast/Interfaces.kt index c7ab887a2..04c46a38e 100644 --- a/compiler/src/prog8/ast/Interfaces.kt +++ b/compiler/src/prog8/ast/Interfaces.kt @@ -6,6 +6,10 @@ import prog8.ast.processing.IAstModifyingVisitor import prog8.ast.processing.IAstVisitor import prog8.ast.statements.* + +// TODO sealed classes instead?? + + interface Node { val position: Position var parent: Node // will be linked correctly later (late init) diff --git a/compiler/src/prog8/ast/antlr/Antr2Kotlin.kt b/compiler/src/prog8/ast/antlr/Antr2Kotlin.kt index 419ec4b4e..4cd1c25fe 100644 --- a/compiler/src/prog8/ast/antlr/Antr2Kotlin.kt +++ b/compiler/src/prog8/ast/antlr/Antr2Kotlin.kt @@ -443,6 +443,10 @@ private fun prog8Parser.ExpressionContext.toAst() : IExpression { // the ConstantFolder takes care of that and converts the type if needed. ReferenceLiteralValue(DataType.ARRAY_UB, array = array, position = litval.toPosition()) } + litval.structliteral()!=null -> { + val values = litval.structliteral().expression().map { it.toAst() } + StructLiteralValue(values, litval.toPosition()) + } else -> throw FatalAstException("invalid parsed literal") } } @@ -518,6 +522,9 @@ private fun prog8Parser.BooleanliteralContext.toAst() = when(text) { private fun prog8Parser.ArrayliteralContext.toAst() : Array = expression().map { it.toAst() }.toTypedArray() +private fun prog8Parser.StructliteralContext.toAst() : Array = + expression().map { it.toAst() }.toTypedArray() + private fun prog8Parser.If_stmtContext.toAst(): IfStatement { val condition = expression().toAst() diff --git a/compiler/src/prog8/ast/expressions/AstExpressions.kt b/compiler/src/prog8/ast/expressions/AstExpressions.kt index 37ef49e4e..549a0ff8d 100644 --- a/compiler/src/prog8/ast/expressions/AstExpressions.kt +++ b/compiler/src/prog8/ast/expressions/AstExpressions.kt @@ -420,6 +420,26 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed } } +class StructLiteralValue(var values: List, + override val position: Position): IExpression { + override lateinit var parent: Node + + override fun linkParents(parent: Node) { + this.parent=parent + values.forEach { it.linkParents(this) } + } + + override fun constValue(program: Program): NumericLiteralValue? = null + override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this) + override fun accept(visitor: IAstVisitor) = visitor.visit(this) + override fun referencesIdentifiers(vararg name: String) = values.any { it.referencesIdentifiers(*name) } + override fun inferType(program: Program) = DataType.STRUCT + + override fun toString(): String { + return "struct{ ${values.joinToString(", ")} }" + } +} + class ReferenceLiteralValue(val type: DataType, // only reference types allowed here val str: String? = null, val array: Array? = null, @@ -428,9 +448,7 @@ class ReferenceLiteralValue(val type: DataType, // only reference types allo override val position: Position) : IExpression { override lateinit var parent: Node - override fun referencesIdentifiers(vararg name: String): Boolean { - return array?.any { it.referencesIdentifiers(*name) } ?: false - } + override fun referencesIdentifiers(vararg name: String) = array?.any { it.referencesIdentifiers(*name) } ?: false val isString = type in StringDatatypes val isArray = type in ArrayDatatypes @@ -443,8 +461,6 @@ class ReferenceLiteralValue(val type: DataType, // only reference types allo if(str==null && heapId==null) throw FatalAstException("literal value missing strvalue/heapId") in ArrayDatatypes -> if(array==null && heapId==null) throw FatalAstException("literal value missing arrayvalue/heapId") -// DataType.STRUCT -> -// if(struct==null && heapId==null) throw FatalAstException("literal value missing structvalue/heapId") else -> throw FatalAstException("invalid type $type") } if(array==null && str==null && heapId==null) diff --git a/compiler/src/prog8/ast/processing/AstChecker.kt b/compiler/src/prog8/ast/processing/AstChecker.kt index 0b50bed27..a0e7db756 100644 --- a/compiler/src/prog8/ast/processing/AstChecker.kt +++ b/compiler/src/prog8/ast/processing/AstChecker.kt @@ -526,6 +526,29 @@ internal class AstChecker(private val program: Program, is NumericLiteralValue -> { checkValueTypeAndRange(decl.datatype, decl.value as NumericLiteralValue) } + is StructLiteralValue -> { + if(decl.datatype==DataType.STRUCT) { + val struct = decl.struct!! + val structLv = decl.value as StructLiteralValue + if(struct.numberOfElements != structLv.values.size) { + checkResult.add(ExpressionError("struct value has incorrect number of elements", structLv.position)) + return + } + for(value in structLv.values.zip(struct.statements)) { + val memberdecl = value.second as VarDecl + val constValue = value.first.constValue(program) + if(constValue==null) { + checkResult.add(ExpressionError("struct literal value for field '${memberdecl.name}' should consist of compile-time constants", value.first.position)) + return + } + val memberDt = memberdecl.datatype + if(!checkValueTypeAndRange(memberDt, constValue)) { + checkResult.add(ExpressionError("struct member value's type is not compatible with member field '${memberdecl.name}'", value.first.position)) + return + } + } + } + } else -> { err("var/const declaration needs a compile-time constant initializer value, or range, instead found: ${decl.value!!.javaClass.simpleName}") super.visit(decl) @@ -1245,21 +1268,12 @@ internal class AstChecker(private val program: Program, DataType.STR -> sourceDatatype== DataType.STR 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() + if(sourceDatatype==DataType.STRUCT) { + val structLv = sourceValue as StructLiteralValue + val numValues = structLv.values.size val targetstruct = target.identifier!!.targetVarDecl(program.namespace)!!.struct!! - return targetstruct.numberOfElements == sourceArraySize + return targetstruct.numberOfElements == numValues } -// if(sourceDatatype==DataType.STRUCT) { -// val sourcename = (sourceValue as IdentifierReference).nameInSource -// val vd1 = program.namespace.lookup(sourcename, target) as? VarDecl -// val targetname = target.identifier!!.nameInSource -// val vd2 = program.namespace.lookup(targetname, target) as? VarDecl -// return vd1?.struct == vd2?.struct -// } false } else -> checkResult.add(SyntaxError("cannot assign new value to variable of type $targetDatatype", position)) diff --git a/compiler/src/prog8/ast/processing/IAstModifyingVisitor.kt b/compiler/src/prog8/ast/processing/IAstModifyingVisitor.kt index 8c5054e46..fee890db0 100644 --- a/compiler/src/prog8/ast/processing/IAstModifyingVisitor.kt +++ b/compiler/src/prog8/ast/processing/IAstModifyingVisitor.kt @@ -225,4 +225,9 @@ interface IAstModifyingVisitor { structDecl.statements = structDecl.statements.map{ it.accept(this) }.toMutableList() return structDecl } + + fun visit(structLv: StructLiteralValue): IExpression { + structLv.values = structLv.values.map { it.accept(this) } + return structLv + } } diff --git a/compiler/src/prog8/ast/processing/IAstVisitor.kt b/compiler/src/prog8/ast/processing/IAstVisitor.kt index 55d448082..7a582bec4 100644 --- a/compiler/src/prog8/ast/processing/IAstVisitor.kt +++ b/compiler/src/prog8/ast/processing/IAstVisitor.kt @@ -173,4 +173,8 @@ interface IAstVisitor { fun visit(structDecl: StructDecl) { structDecl.statements.forEach { it.accept(this) } } + + fun visit(structLv: StructLiteralValue) { + structLv.values.forEach { it.accept(this) } + } } diff --git a/compiler/src/prog8/ast/processing/StatementReorderer.kt b/compiler/src/prog8/ast/processing/StatementReorderer.kt index 9055fd92f..5e97c6de9 100644 --- a/compiler/src/prog8/ast/processing/StatementReorderer.kt +++ b/compiler/src/prog8/ast/processing/StatementReorderer.kt @@ -9,47 +9,41 @@ import prog8.ast.statements.* import prog8.functions.BuiltinFunctions -fun flattenStructAssignment(structAssignment: Assignment, program: Program): List { +fun flattenStructAssignmentFromIdentifier(structAssignment: Assignment, program: Program): List { val identifier = structAssignment.target.identifier!! val identifierName = identifier.nameInSource.single() val targetVar = identifier.targetVarDecl(program.namespace)!! val struct = targetVar.struct!! - val sourceVar = (structAssignment.value as IdentifierReference).targetVarDecl(program.namespace)!! - if(!sourceVar.isArray && sourceVar.struct==null) - throw FatalAstException("can only assign arrays or structs to structs") - if(sourceVar.isArray) { - val sourceArray = (sourceVar.value as ReferenceLiteralValue).array!! - return struct.statements.zip(sourceArray).map { member -> - val decl = member.first as VarDecl - val mangled = mangledStructMemberName(identifierName, decl.name) - val idref = IdentifierReference(listOf(mangled), structAssignment.position) - val assign = Assignment(AssignTarget(null, idref, null, null, structAssignment.position), - null, member.second, member.second.position) - assign.linkParents(structAssignment) - assign + when { + structAssignment.value is IdentifierReference -> { + val sourceVar = (structAssignment.value as IdentifierReference).targetVarDecl(program.namespace)!! + if (sourceVar.struct == null) + throw FatalAstException("can only assign arrays or structs to structs") + // struct memberwise copy + val sourceStruct = sourceVar.struct!! + if(sourceStruct!==targetVar.struct) { + // structs are not the same in assignment + return listOf() // error will be printed elsewhere + } + return struct.statements.zip(sourceStruct.statements).map { member -> + val targetDecl = member.first as VarDecl + val sourceDecl = member.second as VarDecl + if(targetDecl.name != sourceDecl.name) + throw FatalAstException("struct member mismatch") + val mangled = mangledStructMemberName(identifierName, targetDecl.name) + val idref = IdentifierReference(listOf(mangled), structAssignment.position) + val sourcemangled = mangledStructMemberName(sourceVar.name, sourceDecl.name) + val sourceIdref = IdentifierReference(listOf(sourcemangled), structAssignment.position) + val assign = Assignment(AssignTarget(null, idref, null, null, structAssignment.position), + null, sourceIdref, member.second.position) + assign.linkParents(structAssignment) + assign + } } - } - else { - // struct memberwise copy - val sourceStruct = sourceVar.struct!! - if(sourceStruct!==targetVar.struct) { - // structs are not the same in assignment - return listOf() // error will be printed elsewhere - } - return struct.statements.zip(sourceStruct.statements).map { member -> - val targetDecl = member.first as VarDecl - val sourceDecl = member.second as VarDecl - if(targetDecl.name != sourceDecl.name) - throw FatalAstException("struct member mismatch") - val mangled = mangledStructMemberName(identifierName, targetDecl.name) - val idref = IdentifierReference(listOf(mangled), structAssignment.position) - val sourcemangled = mangledStructMemberName(sourceVar.name, sourceDecl.name) - val sourceIdref = IdentifierReference(listOf(sourcemangled), structAssignment.position) - val assign = Assignment(AssignTarget(null, idref, null, null, structAssignment.position), - null, sourceIdref, member.second.position) - assign.linkParents(structAssignment) - assign + structAssignment.value is StructLiteralValue -> { + throw IllegalArgumentException("not going to flatten a structLv assignment here") } + else -> throw FatalAstException("strange struct value") } } @@ -215,52 +209,59 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi } override fun visit(assignment: Assignment): IStatement { + val assg = super.visit(assignment) + if(assg !is Assignment) + return assg + // see if a typecast is needed to convert the value's type into the proper target type - val valuetype = assignment.value.inferType(program) - val targettype = assignment.target.inferType(program, assignment) + val valuetype = assg.value.inferType(program) + val targettype = assg.target.inferType(program, assg) if(targettype!=null && valuetype!=null) { if(valuetype!=targettype) { if (valuetype isAssignableTo targettype) { - assignment.value = TypecastExpression(assignment.value, targettype, true, assignment.value.position) - assignment.value.linkParents(assignment) + assg.value = TypecastExpression(assg.value, targettype, true, assg.value.position) + assg.value.linkParents(assg) } // if they're not assignable, we'll get a proper error later from the AstChecker } } - // struct assignments will be flattened + // struct assignments will be flattened (if it's not a struct literal) if(valuetype==DataType.STRUCT && targettype==DataType.STRUCT) { - val assignments = flattenStructAssignment(assignment, program) + if(assg.value is StructLiteralValue) + return assg // do NOT flatten it at this point!! (the compiler will take care if it, later, if needed) + + val assignments = flattenStructAssignmentFromIdentifier(assg, program) // 'structvar1 = structvar2' if(assignments.isEmpty()) { // something went wrong (probably incompatible struct types) // we'll get an error later from the AstChecker - return assignment + return assg } else { - val scope = AnonymousScope(assignments.toMutableList(), assignment.position) - scope.linkParents(assignment.parent) + val scope = AnonymousScope(assignments.toMutableList(), assg.position) + scope.linkParents(assg.parent) return scope } } - if(assignment.aug_op!=null) { - // transform augmented assignment into normal assignment so we have one case less to deal with later + if(assg.aug_op!=null) { + // transform augmented assg into normal assg so we have one case less to deal with later val newTarget: IExpression = when { - assignment.target.register != null -> RegisterExpr(assignment.target.register!!, assignment.target.position) - assignment.target.identifier != null -> assignment.target.identifier!! - assignment.target.arrayindexed != null -> assignment.target.arrayindexed!! - assignment.target.memoryAddress != null -> DirectMemoryRead(assignment.target.memoryAddress!!.addressExpression, assignment.value.position) - else -> throw FatalAstException("strange assignment") + assg.target.register != null -> RegisterExpr(assg.target.register!!, assg.target.position) + assg.target.identifier != null -> assg.target.identifier!! + assg.target.arrayindexed != null -> assg.target.arrayindexed!! + assg.target.memoryAddress != null -> DirectMemoryRead(assg.target.memoryAddress!!.addressExpression, assg.value.position) + else -> throw FatalAstException("strange assg") } - val expression = BinaryExpression(newTarget, assignment.aug_op.substringBeforeLast('='), assignment.value, assignment.position) - expression.linkParents(assignment.parent) - val convertedAssignment = Assignment(assignment.target, null, expression, assignment.position) - convertedAssignment.linkParents(assignment.parent) + val expression = BinaryExpression(newTarget, assg.aug_op.substringBeforeLast('='), assg.value, assg.position) + expression.linkParents(assg.parent) + val convertedAssignment = Assignment(assg.target, null, expression, assg.position) + convertedAssignment.linkParents(assg.parent) return super.visit(convertedAssignment) } - return super.visit(assignment) + return assg } override fun visit(functionCallStatement: FunctionCallStatement): IStatement { @@ -319,28 +320,6 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi } } - private fun sortConstantAssignmentSequence(first: Assignment, stmtIter: MutableIterator): Pair, IStatement?> { - val sequence= mutableListOf(first) - var trailing: IStatement? = null - while(stmtIter.hasNext()) { - val next = stmtIter.next() - if(next is Assignment) { - val constValue = next.value.constValue(program) - if(constValue==null) { - trailing = next - break - } - sequence.add(next) - } - else { - trailing=next - break - } - } - val sorted = sequence.sortedWith(compareBy({it.value.inferType(program)}, {it.target.shortString(true)})) - return Pair(sorted, trailing) - } - override fun visit(typecast: TypecastExpression): IExpression { // warn about any implicit type casts to Float, because that may not be intended if(typecast.implicit && typecast.type in setOf(DataType.FLOAT, DataType.ARRAY_F)) { @@ -397,4 +376,42 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi } super.visit(memwrite) } + + override fun visit(structLv: StructLiteralValue): IExpression { + val litval = super.visit(structLv) + if(litval !is StructLiteralValue) + return litval + + val decl = litval.parent as? VarDecl + if(decl != null) { + val struct = decl.struct + if(struct != null) { + addTypecastsIfNeeded(litval, struct) + } + } else { + val assign = litval.parent as? Assignment + if (assign != null) { + val decl2 = assign.target.identifier?.targetVarDecl(program.namespace) + if(decl2 != null) { + val struct = decl2.struct + if(struct != null) { + addTypecastsIfNeeded(litval, struct) + } + } + } + } + + return litval + } + + private fun addTypecastsIfNeeded(structLv: StructLiteralValue, struct: StructDecl) { + structLv.values = struct.statements.zip(structLv.values).map { + val memberDt = (it.first as VarDecl).datatype + val valueDt = it.second.inferType(program) + if (valueDt != memberDt) + TypecastExpression(it.second, memberDt, true, it.second.position) + else + it.second + } + } } diff --git a/compiler/src/prog8/ast/statements/AstStatements.kt b/compiler/src/prog8/ast/statements/AstStatements.kt index e7f18573b..0b8ac78db 100644 --- a/compiler/src/prog8/ast/statements/AstStatements.kt +++ b/compiler/src/prog8/ast/statements/AstStatements.kt @@ -225,7 +225,7 @@ class VarDecl(val type: VarDeclType, DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, 0.0, position) else -> throw FatalAstException("can only set a default value for a numeric type") } - val decl = VarDecl(type, declaredDatatype, zeropage, arraysize, name, structName, constValue, isArray, true, position) + val decl = VarDecl(type, declaredDatatype, zeropage, arraysize, name, structName, constValue, isArray, false, position) if(parent!=null) decl.linkParents(parent) return decl @@ -234,7 +234,7 @@ class VarDecl(val type: VarDeclType, fun flattenStructMembers(): MutableList { val result = struct!!.statements.withIndex().map { val member = it.value as VarDecl - val initvalue = if(value!=null) (value as ReferenceLiteralValue).array!![it.index] else null + val initvalue = if(value!=null) (value as StructLiteralValue).values[it.index] else null VarDecl( VarDeclType.VAR, member.datatype, diff --git a/compiler/src/prog8/compiler/AstToSourceCode.kt b/compiler/src/prog8/compiler/AstToSourceCode.kt index fdaf753ee..4623c9fcd 100644 --- a/compiler/src/prog8/compiler/AstToSourceCode.kt +++ b/compiler/src/prog8/compiler/AstToSourceCode.kt @@ -1,16 +1,13 @@ package prog8.compiler +import prog8.ast.* import prog8.ast.antlr.escape -import prog8.ast.IFunctionCall -import prog8.ast.IStatement -import prog8.ast.Module -import prog8.ast.Program import prog8.ast.base.* import prog8.ast.expressions.* import prog8.ast.processing.IAstVisitor import prog8.ast.statements.* -class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor { +class AstToSourceCode(val output: (text: String) -> Unit, val program: Program): IAstVisitor { var scopelevel = 0 fun indent(s: String) = " ".repeat(scopelevel) + s @@ -261,30 +258,42 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor { refLiteral.isString -> output("\"${escape(refLiteral.str!!)}\"") refLiteral.isArray -> { if(refLiteral.array!=null) { - var counter = 0 - output("[") - scopelevel++ - for (v in refLiteral.array) { - v.accept(this) - if (v !== refLiteral.array.last()) - output(", ") - counter++ - if(counter > 16) { - outputln("") - outputi("") - counter=0 - } - } - scopelevel-- - output("]") + outputListMembers(refLiteral.array.asSequence(), '[', ']') } } } } + private fun outputListMembers(array: Sequence, openchar: Char, closechar: Char) { + var counter = 0 + output(openchar.toString()) + scopelevel++ + for (v in array) { + v.accept(this) + if (v !== array.last()) + output(", ") + counter++ + if (counter > 16) { + outputln("") + outputi("") + counter = 0 + } + } + scopelevel-- + output(closechar.toString()) + } + override fun visit(assignment: Assignment) { + if(assignment is VariableInitializationAssignment) { + val targetVar = assignment.target.identifier?.targetVarDecl(program.namespace) + if(targetVar?.struct != null) { + // skip STRUCT init assignments + return + } + } + assignment.target.accept(this) - if(assignment.aug_op!=null) + if (assignment.aug_op != null) output(" ${assignment.aug_op} ") else output(" = ") @@ -432,6 +441,11 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor { whenChoice.statements.accept(this) outputln("") } + + override fun visit(structLv: StructLiteralValue) { + outputListMembers(structLv.values.asSequence(), '{', '}') + } + override fun visit(nopStatement: NopStatement) { output("; NOP @ ${nopStatement.position} $nopStatement") } diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 0cd24ba03..e89c94b0c 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -4,7 +4,6 @@ import prog8.ast.* import prog8.ast.base.* import prog8.ast.base.RegisterOrPair.* import prog8.ast.expressions.* -import prog8.ast.processing.flattenStructAssignment import prog8.ast.statements.* import prog8.compiler.intermediate.IntermediateProgram import prog8.compiler.intermediate.Opcode @@ -593,6 +592,7 @@ internal class Compiler(private val program: Program) { is TypecastExpression -> translate(expr) is DirectMemoryRead -> translate(expr) is AddressOf -> translate(expr) + is StructLiteralValue -> throw CompilerException("a struct Lv should have been flattened as assignments") else -> { val lv = expr.constValue(program) ?: throw CompilerException("constant expression required, not $expr") when(lv.type) { @@ -1404,6 +1404,26 @@ internal class Compiler(private val program: Program) { private fun translate(stmt: Assignment) { prog.line(stmt.position) + if(stmt.value is StructLiteralValue) { + // flatten into individual struct member assignments + val identifier = stmt.target.identifier!! + val identifierName = identifier.nameInSource.single() + val targetVar = identifier.targetVarDecl(program.namespace)!! + val struct = targetVar.struct!! + val sourcevalues = (stmt.value as StructLiteralValue).values + val assignments = struct.statements.zip(sourcevalues).map { member -> + val decl = member.first as VarDecl + val mangled = mangledStructMemberName(identifierName, decl.name) + val idref = IdentifierReference(listOf(mangled), stmt.position) + val assign = Assignment(AssignTarget(null, idref, null, null, stmt.position), + null, member.second, member.second.position) + assign.linkParents(stmt) + assign + } + assignments.forEach { translate(it) } + return + } + translate(stmt.value) val valueDt = stmt.value.inferType(program) @@ -1448,11 +1468,6 @@ 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. - flattenStructAssignment(stmt, program).forEach { translate(it) } - 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") diff --git a/compiler/src/prog8/compiler/Main.kt b/compiler/src/prog8/compiler/Main.kt index b2921b015..5c7115162 100644 --- a/compiler/src/prog8/compiler/Main.kt +++ b/compiler/src/prog8/compiler/Main.kt @@ -17,7 +17,6 @@ import prog8.parser.importModule import prog8.parser.moduleName import java.io.File import java.io.PrintStream -import java.lang.Exception import java.nio.file.Path import kotlin.system.exitProcess import kotlin.system.measureTimeMillis @@ -146,7 +145,7 @@ fun compileProgram(filepath: Path, fun printAst(programAst: Program) { println() - val printer = AstToSourceCode(::print) + val printer = AstToSourceCode(::print, programAst) printer.visit(programAst) println() } diff --git a/compiler/src/prog8/functions/BuiltinFunctions.kt b/compiler/src/prog8/functions/BuiltinFunctions.kt index 88e7776fc..0a1c6a3a3 100644 --- a/compiler/src/prog8/functions/BuiltinFunctions.kt +++ b/compiler/src/prog8/functions/BuiltinFunctions.kt @@ -261,7 +261,7 @@ private fun builtinLen(args: List, position: Position, program: Pro return NumericLiteralValue.optimalInteger(arraySize, position) if(args[0] !is IdentifierReference) throw SyntaxError("len argument should be an identifier, but is ${args[0]}", position) - val target = (args[0] as IdentifierReference).targetStatement(program.namespace) as VarDecl + val target = (args[0] as IdentifierReference).targetVarDecl(program.namespace)!! return when(target.datatype) { DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> { diff --git a/examples/test.p8 b/examples/test.p8 index 19dbcbc51..20740f376 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -12,11 +12,18 @@ } sub start() { - Color rgb1 + Color rgb1 = {1,2,3.44} Color rgb2 + rgb2 = {22233, 33, 1.1} ; @todo implicit type conversion c64scr.print_b(rgb1.green) + c64.CHROUT('\n') c64scr.print_b(rgb2.green) + c64.CHROUT('\n') + + rgb1=rgb2 + c64scr.print_b(rgb1.green) + c64.CHROUT('\n') } } diff --git a/parser/antlr/prog8.g4 b/parser/antlr/prog8.g4 index 5cc774e24..4e4ff7b51 100644 --- a/parser/antlr/prog8.g4 +++ b/parser/antlr/prog8.g4 @@ -214,7 +214,9 @@ wordsuffix : '.w' ; booleanliteral : 'true' | 'false' ; -arrayliteral : '[' EOL? expression (',' EOL? expression)* EOL? ']' ; // you can split the array list over several lines +arrayliteral : '[' EOL? expression (',' EOL? expression)* EOL? ']' ; // you can split the values over several lines + +structliteral : '{' EOL? expression (',' EOL? expression)* EOL? '}' ; // you can split the values over several lines stringliteral : STRING ; @@ -222,6 +224,7 @@ charliteral : SINGLECHAR ; floatliteral : FLOAT_NUMBER ; + literalvalue : integerliteral | booleanliteral @@ -229,6 +232,7 @@ literalvalue : | stringliteral | charliteral | floatliteral + | structliteral ; inlineasm : '%asm' INLINEASMBLOCK;