mirror of
https://github.com/irmen/prog8.git
synced 2024-12-25 23:29:55 +00:00
struct literals
This commit is contained in:
parent
17be722e2b
commit
61af72b906
@ -6,6 +6,10 @@ import prog8.ast.processing.IAstModifyingVisitor
|
|||||||
import prog8.ast.processing.IAstVisitor
|
import prog8.ast.processing.IAstVisitor
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
|
|
||||||
|
|
||||||
|
// TODO sealed classes instead??
|
||||||
|
|
||||||
|
|
||||||
interface Node {
|
interface Node {
|
||||||
val position: Position
|
val position: Position
|
||||||
var parent: Node // will be linked correctly later (late init)
|
var parent: Node // will be linked correctly later (late init)
|
||||||
|
@ -443,6 +443,10 @@ private fun prog8Parser.ExpressionContext.toAst() : IExpression {
|
|||||||
// the ConstantFolder takes care of that and converts the type if needed.
|
// the ConstantFolder takes care of that and converts the type if needed.
|
||||||
ReferenceLiteralValue(DataType.ARRAY_UB, array = array, position = litval.toPosition())
|
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")
|
else -> throw FatalAstException("invalid parsed literal")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -518,6 +522,9 @@ private fun prog8Parser.BooleanliteralContext.toAst() = when(text) {
|
|||||||
private fun prog8Parser.ArrayliteralContext.toAst() : Array<IExpression> =
|
private fun prog8Parser.ArrayliteralContext.toAst() : Array<IExpression> =
|
||||||
expression().map { it.toAst() }.toTypedArray()
|
expression().map { it.toAst() }.toTypedArray()
|
||||||
|
|
||||||
|
private fun prog8Parser.StructliteralContext.toAst() : Array<IExpression> =
|
||||||
|
expression().map { it.toAst() }.toTypedArray()
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.If_stmtContext.toAst(): IfStatement {
|
private fun prog8Parser.If_stmtContext.toAst(): IfStatement {
|
||||||
val condition = expression().toAst()
|
val condition = expression().toAst()
|
||||||
|
@ -420,6 +420,26 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class StructLiteralValue(var values: List<IExpression>,
|
||||||
|
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
|
class ReferenceLiteralValue(val type: DataType, // only reference types allowed here
|
||||||
val str: String? = null,
|
val str: String? = null,
|
||||||
val array: Array<IExpression>? = null,
|
val array: Array<IExpression>? = null,
|
||||||
@ -428,9 +448,7 @@ class ReferenceLiteralValue(val type: DataType, // only reference types allo
|
|||||||
override val position: Position) : IExpression {
|
override val position: Position) : IExpression {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String): Boolean {
|
override fun referencesIdentifiers(vararg name: String) = array?.any { it.referencesIdentifiers(*name) } ?: false
|
||||||
return array?.any { it.referencesIdentifiers(*name) } ?: false
|
|
||||||
}
|
|
||||||
|
|
||||||
val isString = type in StringDatatypes
|
val isString = type in StringDatatypes
|
||||||
val isArray = type in ArrayDatatypes
|
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")
|
if(str==null && heapId==null) throw FatalAstException("literal value missing strvalue/heapId")
|
||||||
in ArrayDatatypes ->
|
in ArrayDatatypes ->
|
||||||
if(array==null && heapId==null) throw FatalAstException("literal value missing arrayvalue/heapId")
|
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")
|
else -> throw FatalAstException("invalid type $type")
|
||||||
}
|
}
|
||||||
if(array==null && str==null && heapId==null)
|
if(array==null && str==null && heapId==null)
|
||||||
|
@ -526,6 +526,29 @@ internal class AstChecker(private val program: Program,
|
|||||||
is NumericLiteralValue -> {
|
is NumericLiteralValue -> {
|
||||||
checkValueTypeAndRange(decl.datatype, decl.value as 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 -> {
|
else -> {
|
||||||
err("var/const declaration needs a compile-time constant initializer value, or range, instead found: ${decl.value!!.javaClass.simpleName}")
|
err("var/const declaration needs a compile-time constant initializer value, or range, instead found: ${decl.value!!.javaClass.simpleName}")
|
||||||
super.visit(decl)
|
super.visit(decl)
|
||||||
@ -1245,21 +1268,12 @@ internal class AstChecker(private val program: Program,
|
|||||||
DataType.STR -> sourceDatatype== DataType.STR
|
DataType.STR -> sourceDatatype== DataType.STR
|
||||||
DataType.STR_S -> sourceDatatype== DataType.STR_S
|
DataType.STR_S -> sourceDatatype== DataType.STR_S
|
||||||
DataType.STRUCT -> {
|
DataType.STRUCT -> {
|
||||||
// for now we've decided you cannot assign struct by-value.
|
if(sourceDatatype==DataType.STRUCT) {
|
||||||
// but you can however assign an array to it of the correct size
|
val structLv = sourceValue as StructLiteralValue
|
||||||
if(sourceDatatype in ArrayDatatypes) {
|
val numValues = structLv.values.size
|
||||||
val identifier = sourceValue as IdentifierReference
|
|
||||||
val sourceArraySize = identifier.targetVarDecl(program.namespace)!!.arraysize?.size()
|
|
||||||
val targetstruct = target.identifier!!.targetVarDecl(program.namespace)!!.struct!!
|
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
|
false
|
||||||
}
|
}
|
||||||
else -> checkResult.add(SyntaxError("cannot assign new value to variable of type $targetDatatype", position))
|
else -> checkResult.add(SyntaxError("cannot assign new value to variable of type $targetDatatype", position))
|
||||||
|
@ -225,4 +225,9 @@ interface IAstModifyingVisitor {
|
|||||||
structDecl.statements = structDecl.statements.map{ it.accept(this) }.toMutableList()
|
structDecl.statements = structDecl.statements.map{ it.accept(this) }.toMutableList()
|
||||||
return structDecl
|
return structDecl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun visit(structLv: StructLiteralValue): IExpression {
|
||||||
|
structLv.values = structLv.values.map { it.accept(this) }
|
||||||
|
return structLv
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -173,4 +173,8 @@ interface IAstVisitor {
|
|||||||
fun visit(structDecl: StructDecl) {
|
fun visit(structDecl: StructDecl) {
|
||||||
structDecl.statements.forEach { it.accept(this) }
|
structDecl.statements.forEach { it.accept(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun visit(structLv: StructLiteralValue) {
|
||||||
|
structLv.values.forEach { it.accept(this) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,27 +9,16 @@ import prog8.ast.statements.*
|
|||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
|
|
||||||
|
|
||||||
fun flattenStructAssignment(structAssignment: Assignment, program: Program): List<Assignment> {
|
fun flattenStructAssignmentFromIdentifier(structAssignment: Assignment, program: Program): List<Assignment> {
|
||||||
val identifier = structAssignment.target.identifier!!
|
val identifier = structAssignment.target.identifier!!
|
||||||
val identifierName = identifier.nameInSource.single()
|
val identifierName = identifier.nameInSource.single()
|
||||||
val targetVar = identifier.targetVarDecl(program.namespace)!!
|
val targetVar = identifier.targetVarDecl(program.namespace)!!
|
||||||
val struct = targetVar.struct!!
|
val struct = targetVar.struct!!
|
||||||
|
when {
|
||||||
|
structAssignment.value is IdentifierReference -> {
|
||||||
val sourceVar = (structAssignment.value as IdentifierReference).targetVarDecl(program.namespace)!!
|
val sourceVar = (structAssignment.value as IdentifierReference).targetVarDecl(program.namespace)!!
|
||||||
if(!sourceVar.isArray && sourceVar.struct==null)
|
if (sourceVar.struct == null)
|
||||||
throw FatalAstException("can only assign arrays or structs to structs")
|
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// struct memberwise copy
|
// struct memberwise copy
|
||||||
val sourceStruct = sourceVar.struct!!
|
val sourceStruct = sourceVar.struct!!
|
||||||
if(sourceStruct!==targetVar.struct) {
|
if(sourceStruct!==targetVar.struct) {
|
||||||
@ -51,6 +40,11 @@ fun flattenStructAssignment(structAssignment: Assignment, program: Program): Lis
|
|||||||
assign
|
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 {
|
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
|
// see if a typecast is needed to convert the value's type into the proper target type
|
||||||
val valuetype = assignment.value.inferType(program)
|
val valuetype = assg.value.inferType(program)
|
||||||
val targettype = assignment.target.inferType(program, assignment)
|
val targettype = assg.target.inferType(program, assg)
|
||||||
if(targettype!=null && valuetype!=null) {
|
if(targettype!=null && valuetype!=null) {
|
||||||
if(valuetype!=targettype) {
|
if(valuetype!=targettype) {
|
||||||
if (valuetype isAssignableTo targettype) {
|
if (valuetype isAssignableTo targettype) {
|
||||||
assignment.value = TypecastExpression(assignment.value, targettype, true, assignment.value.position)
|
assg.value = TypecastExpression(assg.value, targettype, true, assg.value.position)
|
||||||
assignment.value.linkParents(assignment)
|
assg.value.linkParents(assg)
|
||||||
}
|
}
|
||||||
// if they're not assignable, we'll get a proper error later from the AstChecker
|
// 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) {
|
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()) {
|
if(assignments.isEmpty()) {
|
||||||
// something went wrong (probably incompatible struct types)
|
// something went wrong (probably incompatible struct types)
|
||||||
// we'll get an error later from the AstChecker
|
// we'll get an error later from the AstChecker
|
||||||
return assignment
|
return assg
|
||||||
} else {
|
} else {
|
||||||
val scope = AnonymousScope(assignments.toMutableList(), assignment.position)
|
val scope = AnonymousScope(assignments.toMutableList(), assg.position)
|
||||||
scope.linkParents(assignment.parent)
|
scope.linkParents(assg.parent)
|
||||||
return scope
|
return scope
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(assignment.aug_op!=null) {
|
if(assg.aug_op!=null) {
|
||||||
// transform augmented assignment into normal assignment so we have one case less to deal with later
|
// transform augmented assg into normal assg so we have one case less to deal with later
|
||||||
val newTarget: IExpression =
|
val newTarget: IExpression =
|
||||||
when {
|
when {
|
||||||
assignment.target.register != null -> RegisterExpr(assignment.target.register!!, assignment.target.position)
|
assg.target.register != null -> RegisterExpr(assg.target.register!!, assg.target.position)
|
||||||
assignment.target.identifier != null -> assignment.target.identifier!!
|
assg.target.identifier != null -> assg.target.identifier!!
|
||||||
assignment.target.arrayindexed != null -> assignment.target.arrayindexed!!
|
assg.target.arrayindexed != null -> assg.target.arrayindexed!!
|
||||||
assignment.target.memoryAddress != null -> DirectMemoryRead(assignment.target.memoryAddress!!.addressExpression, assignment.value.position)
|
assg.target.memoryAddress != null -> DirectMemoryRead(assg.target.memoryAddress!!.addressExpression, assg.value.position)
|
||||||
else -> throw FatalAstException("strange assignment")
|
else -> throw FatalAstException("strange assg")
|
||||||
}
|
}
|
||||||
|
|
||||||
val expression = BinaryExpression(newTarget, assignment.aug_op.substringBeforeLast('='), assignment.value, assignment.position)
|
val expression = BinaryExpression(newTarget, assg.aug_op.substringBeforeLast('='), assg.value, assg.position)
|
||||||
expression.linkParents(assignment.parent)
|
expression.linkParents(assg.parent)
|
||||||
val convertedAssignment = Assignment(assignment.target, null, expression, assignment.position)
|
val convertedAssignment = Assignment(assg.target, null, expression, assg.position)
|
||||||
convertedAssignment.linkParents(assignment.parent)
|
convertedAssignment.linkParents(assg.parent)
|
||||||
return super.visit(convertedAssignment)
|
return super.visit(convertedAssignment)
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.visit(assignment)
|
return assg
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(functionCallStatement: FunctionCallStatement): IStatement {
|
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<IStatement>): Pair<List<Assignment>, 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 {
|
override fun visit(typecast: TypecastExpression): IExpression {
|
||||||
// warn about any implicit type casts to Float, because that may not be intended
|
// 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)) {
|
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)
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -225,7 +225,7 @@ class VarDecl(val type: VarDeclType,
|
|||||||
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, 0.0, position)
|
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, 0.0, position)
|
||||||
else -> throw FatalAstException("can only set a default value for a numeric type")
|
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)
|
if(parent!=null)
|
||||||
decl.linkParents(parent)
|
decl.linkParents(parent)
|
||||||
return decl
|
return decl
|
||||||
@ -234,7 +234,7 @@ class VarDecl(val type: VarDeclType,
|
|||||||
fun flattenStructMembers(): MutableList<IStatement> {
|
fun flattenStructMembers(): MutableList<IStatement> {
|
||||||
val result = struct!!.statements.withIndex().map {
|
val result = struct!!.statements.withIndex().map {
|
||||||
val member = it.value as VarDecl
|
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(
|
VarDecl(
|
||||||
VarDeclType.VAR,
|
VarDeclType.VAR,
|
||||||
member.datatype,
|
member.datatype,
|
||||||
|
@ -1,16 +1,13 @@
|
|||||||
package prog8.compiler
|
package prog8.compiler
|
||||||
|
|
||||||
|
import prog8.ast.*
|
||||||
import prog8.ast.antlr.escape
|
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.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.processing.IAstVisitor
|
import prog8.ast.processing.IAstVisitor
|
||||||
import prog8.ast.statements.*
|
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
|
var scopelevel = 0
|
||||||
|
|
||||||
fun indent(s: String) = " ".repeat(scopelevel) + s
|
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.isString -> output("\"${escape(refLiteral.str!!)}\"")
|
||||||
refLiteral.isArray -> {
|
refLiteral.isArray -> {
|
||||||
if(refLiteral.array!=null) {
|
if(refLiteral.array!=null) {
|
||||||
var counter = 0
|
outputListMembers(refLiteral.array.asSequence(), '[', ']')
|
||||||
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("]")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun outputListMembers(array: Sequence<IExpression>, 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) {
|
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)
|
assignment.target.accept(this)
|
||||||
if(assignment.aug_op!=null)
|
if (assignment.aug_op != null)
|
||||||
output(" ${assignment.aug_op} ")
|
output(" ${assignment.aug_op} ")
|
||||||
else
|
else
|
||||||
output(" = ")
|
output(" = ")
|
||||||
@ -432,6 +441,11 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
|
|||||||
whenChoice.statements.accept(this)
|
whenChoice.statements.accept(this)
|
||||||
outputln("")
|
outputln("")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun visit(structLv: StructLiteralValue) {
|
||||||
|
outputListMembers(structLv.values.asSequence(), '{', '}')
|
||||||
|
}
|
||||||
|
|
||||||
override fun visit(nopStatement: NopStatement) {
|
override fun visit(nopStatement: NopStatement) {
|
||||||
output("; NOP @ ${nopStatement.position} $nopStatement")
|
output("; NOP @ ${nopStatement.position} $nopStatement")
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import prog8.ast.*
|
|||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.base.RegisterOrPair.*
|
import prog8.ast.base.RegisterOrPair.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.processing.flattenStructAssignment
|
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.intermediate.IntermediateProgram
|
import prog8.compiler.intermediate.IntermediateProgram
|
||||||
import prog8.compiler.intermediate.Opcode
|
import prog8.compiler.intermediate.Opcode
|
||||||
@ -593,6 +592,7 @@ internal class Compiler(private val program: Program) {
|
|||||||
is TypecastExpression -> translate(expr)
|
is TypecastExpression -> translate(expr)
|
||||||
is DirectMemoryRead -> translate(expr)
|
is DirectMemoryRead -> translate(expr)
|
||||||
is AddressOf -> translate(expr)
|
is AddressOf -> translate(expr)
|
||||||
|
is StructLiteralValue -> throw CompilerException("a struct Lv should have been flattened as assignments")
|
||||||
else -> {
|
else -> {
|
||||||
val lv = expr.constValue(program) ?: throw CompilerException("constant expression required, not $expr")
|
val lv = expr.constValue(program) ?: throw CompilerException("constant expression required, not $expr")
|
||||||
when(lv.type) {
|
when(lv.type) {
|
||||||
@ -1404,6 +1404,26 @@ internal class Compiler(private val program: Program) {
|
|||||||
|
|
||||||
private fun translate(stmt: Assignment) {
|
private fun translate(stmt: Assignment) {
|
||||||
prog.line(stmt.position)
|
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)
|
translate(stmt.value)
|
||||||
|
|
||||||
val valueDt = stmt.value.inferType(program)
|
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")
|
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 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")
|
in ArrayDatatypes -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt")
|
||||||
else -> throw CompilerException("weird/unknown targetdt")
|
else -> throw CompilerException("weird/unknown targetdt")
|
||||||
|
@ -17,7 +17,6 @@ import prog8.parser.importModule
|
|||||||
import prog8.parser.moduleName
|
import prog8.parser.moduleName
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.PrintStream
|
import java.io.PrintStream
|
||||||
import java.lang.Exception
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
@ -146,7 +145,7 @@ fun compileProgram(filepath: Path,
|
|||||||
|
|
||||||
fun printAst(programAst: Program) {
|
fun printAst(programAst: Program) {
|
||||||
println()
|
println()
|
||||||
val printer = AstToSourceCode(::print)
|
val printer = AstToSourceCode(::print, programAst)
|
||||||
printer.visit(programAst)
|
printer.visit(programAst)
|
||||||
println()
|
println()
|
||||||
}
|
}
|
||||||
|
@ -261,7 +261,7 @@ private fun builtinLen(args: List<IExpression>, position: Position, program: Pro
|
|||||||
return NumericLiteralValue.optimalInteger(arraySize, position)
|
return NumericLiteralValue.optimalInteger(arraySize, position)
|
||||||
if(args[0] !is IdentifierReference)
|
if(args[0] !is IdentifierReference)
|
||||||
throw SyntaxError("len argument should be an identifier, but is ${args[0]}", position)
|
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) {
|
return when(target.datatype) {
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||||
|
@ -12,11 +12,18 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
Color rgb1
|
Color rgb1 = {1,2,3.44}
|
||||||
Color rgb2
|
Color rgb2
|
||||||
|
|
||||||
|
rgb2 = {22233, 33, 1.1} ; @todo implicit type conversion
|
||||||
c64scr.print_b(rgb1.green)
|
c64scr.print_b(rgb1.green)
|
||||||
|
c64.CHROUT('\n')
|
||||||
c64scr.print_b(rgb2.green)
|
c64scr.print_b(rgb2.green)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
|
||||||
|
rgb1=rgb2
|
||||||
|
c64scr.print_b(rgb1.green)
|
||||||
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -214,7 +214,9 @@ wordsuffix : '.w' ;
|
|||||||
|
|
||||||
booleanliteral : 'true' | 'false' ;
|
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 ;
|
stringliteral : STRING ;
|
||||||
|
|
||||||
@ -222,6 +224,7 @@ charliteral : SINGLECHAR ;
|
|||||||
|
|
||||||
floatliteral : FLOAT_NUMBER ;
|
floatliteral : FLOAT_NUMBER ;
|
||||||
|
|
||||||
|
|
||||||
literalvalue :
|
literalvalue :
|
||||||
integerliteral
|
integerliteral
|
||||||
| booleanliteral
|
| booleanliteral
|
||||||
@ -229,6 +232,7 @@ literalvalue :
|
|||||||
| stringliteral
|
| stringliteral
|
||||||
| charliteral
|
| charliteral
|
||||||
| floatliteral
|
| floatliteral
|
||||||
|
| structliteral
|
||||||
;
|
;
|
||||||
|
|
||||||
inlineasm : '%asm' INLINEASMBLOCK;
|
inlineasm : '%asm' INLINEASMBLOCK;
|
||||||
|
Loading…
Reference in New Issue
Block a user