struct fixes

This commit is contained in:
Irmen de Jong 2019-07-12 16:38:04 +02:00
parent 9f6fa60bf1
commit 717b5f3b07
16 changed files with 130 additions and 112 deletions

View File

@ -571,7 +571,7 @@ private fun prog8Parser.When_choiceContext.toAst(): WhenChoice {
private fun prog8Parser.VardeclContext.toAst(): VarDecl {
return VarDecl(
if(structname!=null) VarDeclType.STRUCT else VarDeclType.VAR,
VarDeclType.VAR,
datatype()?.toAst() ?: DataType.STRUCT,
ZEROPAGE() != null,
arrayindex()?.toAst(),

View File

@ -94,8 +94,7 @@ enum class BranchCondition {
enum class VarDeclType {
VAR,
CONST,
MEMORY,
STRUCT
MEMORY
}
val IterableDatatypes = setOf(

View File

@ -6,7 +6,7 @@ import prog8.ast.processing.*
import prog8.ast.statements.Assignment
import prog8.ast.statements.ForLoop
import prog8.compiler.CompilationOptions
import prog8.optimizer.RemoveNops
import prog8.optimizer.FlattenAnonymousScopesAndRemoveNops
// the name of the subroutine that should be called for every block to initialize its variables
@ -17,9 +17,9 @@ internal const val initvarsSubName="prog8_init_vars"
internal const val autoHeapValuePrefix = "auto_heap_value_"
internal fun Program.removeNops() {
val remover = RemoveNops()
remover.visit(this)
internal fun Program.removeNopsFlattenAnonScopes() {
val flattener = FlattenAnonymousScopesAndRemoveNops()
flattener.visit(this)
}

View File

@ -489,6 +489,14 @@ internal class AstChecker(private val program: Program,
when(decl.type) {
VarDeclType.VAR, VarDeclType.CONST -> {
if(decl.struct!=null || decl.datatype==DataType.STRUCT) {
if(decl.datatype!=DataType.STRUCT)
throw FatalAstException("struct vardecl should be of data type struct $decl")
if(decl.struct==null)
throw FatalAstException("struct vardecl should be linked to its struct $decl")
if(decl.zeropage)
err("struct can not be in zeropage")
}
if (decl.value == null) {
when {
decl.datatype in NumericDatatypes -> {
@ -502,6 +510,9 @@ internal class AstChecker(private val program: Program,
litVal.parent = decl
decl.value = litVal
}
decl.datatype == DataType.STRUCT -> {
// TODO structs are not initialized with a literal value yet, should be an array of zeros!
}
decl.type== VarDeclType.VAR -> {
val litVal = LiteralValue(decl.datatype, initHeapId = heapStringSentinel, position = decl.position) // point to the sentinel heap value instead
litVal.parent=decl
@ -558,16 +569,6 @@ internal class AstChecker(private val program: Program,
}
}
}
VarDeclType.STRUCT -> {
if(decl.struct==null)
throw FatalAstException("struct vardecl should be linked to its struct $decl")
if(decl.datatype!=DataType.STRUCT)
throw FatalAstException("struct vardecl should be of data type struct $decl")
if(decl.zeropage)
err("struct can not be in zeropage")
if(decl.value!=null)
err("struct can not have an initialization value") // TODO allow struct to have initalization values
}
}
return super.visit(decl)
@ -1274,7 +1275,7 @@ internal class AstChecker(private val program: Program,
else {
if(decl.zeropage)
checkResult.add(SyntaxError("struct can not contain zeropage members", decl.position))
if(decl.type == VarDeclType.STRUCT)
if(decl.datatype==DataType.STRUCT)
checkResult.add(SyntaxError("structs can not be nested", decl.position))
}
}

View File

@ -58,7 +58,7 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
// is it a struct variable? then define all its struct members as mangled names,
// and include the original decl as well.
if(decl.type==VarDeclType.STRUCT) {
if(decl.datatype==DataType.STRUCT) {
if(decl.structHasBeenFlattened)
return decl // don't do this multiple times

View File

@ -8,6 +8,8 @@ import prog8.ast.expressions.FunctionCall
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.LiteralValue
import prog8.ast.statements.*
import prog8.compiler.CompilerException
internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope): IAstModifyingVisitor {
// For VarDecls that declare an initialization value:
@ -59,6 +61,24 @@ internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope
decl.position
)
}
if(decl.datatype==DataType.STRUCT) {
// a struct initialization value
// flatten it to assignment statements
val sourceArray = (decl.value as LiteralValue).arrayvalue!!
val memberAssignments = decl.struct!!.statements.zip(sourceArray).map { member ->
val memberDecl = member.first as VarDecl
val mangled = mangledStructMemberName(decl.name, memberDecl.name)
val idref = IdentifierReference(listOf(mangled), decl.position)
val target = AssignTarget(null, idref, null, null, decl.position)
val assign = VariableInitializationAssignment(target, null, member.second, member.second.position)
assign
}
val scope = AnonymousScope(memberAssignments.toMutableList(), decl.position)
scope.linkParents(decl.parent)
return scope
}
return decl
}

View File

@ -103,16 +103,10 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
}
override fun visit(decl: VarDecl) {
// if(decl.hiddenButDoNotRemove) {
// // skip autogenerated vardecl
// return
// }
when(decl.type) {
VarDeclType.VAR -> {}
VarDeclType.CONST -> output("const ")
VarDeclType.MEMORY -> output("&")
VarDeclType.STRUCT -> output("${decl.struct!!.name} ")
}
output(decl.struct?.name ?: "")
output(datatypeString(decl.datatype))
@ -185,7 +179,7 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
private fun outputStatements(statements: List<IStatement>) {
for(stmt in statements) {
if(stmt is VarDecl && stmt.hiddenButDoNotRemove)
continue // skip autogenerated decls
continue // skip autogenerated decls (to avoid generating a newline)
outputi("")
stmt.accept(this)
output("\n")
@ -436,6 +430,6 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
outputln("")
}
override fun visit(nopStatement: NopStatement) {
output("; NOP")
output("; NOP @ ${nopStatement.position} $nopStatement")
}
}

View File

@ -682,7 +682,6 @@ internal class Compiler(private val program: Program) {
else -> throw CompilerException("invalid datatype for memory variable expression: $target")
}
}
VarDeclType.STRUCT -> TODO("decltype struct")
}
}
@ -1448,23 +1447,7 @@ internal class Compiler(private val program: Program) {
}
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)
}
flattenStructAssignment(stmt, program).forEach { translate(it) }
return
}
in StringDatatypes -> throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt")
@ -1481,6 +1464,26 @@ internal class Compiler(private val program: Program) {
popValueIntoTarget(stmt.target, datatype)
}
private fun flattenStructAssignment(structAssignment: Assignment, program: Program): List<Assignment> {
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)
throw CompilerException("can only assign arrays to structs")
val sourceArray = (sourceVar.value as LiteralValue).arrayvalue!!
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
}
}
private fun pushHeapVarAddress(value: IExpression, removeLastOpcode: Boolean) {
when (value) {
is LiteralValue -> throw CompilerException("can only push address of string or array (value on the heap)")
@ -1520,9 +1523,6 @@ 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 $assignTarget")
}
}
} else throw CompilerException("invalid assignment target type ${target::class}")
}

View File

@ -84,7 +84,7 @@ fun compileProgram(filepath: Path,
}
}
programAst.removeNops()
programAst.removeNopsFlattenAnonScopes()
programAst.checkValid(compilerOptions) // check if final tree is valid
programAst.checkRecursion() // check if there are recursive subroutine calls

View File

@ -415,6 +415,10 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
throw CompilerException("array should already be in the heap")
RuntimeValue(decl.datatype, heapId = litval.heapId)
}
DataType.STRUCT -> {
// struct variables have been flattened already
return
}
else -> throw CompilerException("weird datatype")
}
currentBlock.variables[scopedname] = value
@ -435,9 +439,6 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
if(lv.type in IntegerDatatypes)
currentBlock.memoryPointers[scopedname] = Pair(lv.asIntegerValue!!, decl.datatype)
}
VarDeclType.STRUCT -> {
// the struct decl itself will be replaced by mangled declarations for each of their members.
}
}
}

View File

@ -31,14 +31,6 @@ internal fun Program.constantFold() {
internal fun Program.optimizeStatements(optimizeInlining: Boolean): Int {
val optimizer = StatementOptimizer(this, optimizeInlining)
optimizer.visit(this)
for(scope in optimizer.scopesToFlatten.reversed()) {
val namescope = scope.parent as INameScope
val idx = namescope.statements.indexOf(scope as IStatement)
if(idx>=0) {
namescope.statements[idx] = NopStatement.insteadOf(namescope.statements[idx])
namescope.statements.addAll(idx, scope.statements)
}
}
modules.forEach { it.linkParents(this.namespace) } // re-link in final configuration
return optimizer.optimizationsDone

View File

@ -20,7 +20,6 @@ import kotlin.math.floor
internal class StatementOptimizer(private val program: Program, private val optimizeInlining: Boolean) : IAstModifyingVisitor {
var optimizationsDone: Int = 0
private set
var scopesToFlatten = mutableListOf<INameScope>()
private val pureBuiltinFunctions = BuiltinFunctions.filter { it.value.pure }
private val callgraph = CallGraph(program)
@ -621,11 +620,6 @@ internal class StatementOptimizer(private val program: Program, private val opti
if(linesToRemove.isNotEmpty()) {
linesToRemove.reversed().forEach{scope.statements.removeAt(it)}
}
if(scope.parent is INameScope) {
scopesToFlatten.add(scope) // get rid of the anonymous scope
}
return super.visit(scope)
}
@ -642,17 +636,38 @@ internal class StatementOptimizer(private val program: Program, private val opti
internal class RemoveNops: IAstVisitor {
val nopStatements = mutableListOf<NopStatement>()
internal class FlattenAnonymousScopesAndRemoveNops: IAstVisitor {
private var scopesToFlatten = mutableListOf<INameScope>()
private val nopStatements = mutableListOf<NopStatement>()
override fun visit(program: Program) {
super.visit(program)
// at the end, remove the encountered NOP statements
for(scope in scopesToFlatten.reversed()) {
val namescope = scope.parent as INameScope
val idx = namescope.statements.indexOf(scope as IStatement)
if(idx>=0) {
val nop = NopStatement.insteadOf(namescope.statements[idx])
nop.parent = namescope as Node
namescope.statements[idx] = nop
namescope.statements.addAll(idx, scope.statements)
scope.statements.forEach { it.parent = namescope as Node }
visit(nop)
}
}
this.nopStatements.forEach {
it.definingScope().remove(it)
}
}
override fun visit(scope: AnonymousScope) {
if(scope.parent is INameScope) {
scopesToFlatten.add(scope) // get rid of the anonymous scope
}
return super.visit(scope)
}
override fun visit(nopStatement: NopStatement) {
nopStatements.add(nopStatement)
}

View File

@ -396,7 +396,6 @@ class AstVm(val program: Program) {
mem.setUByte(addr,newval.toShort())
}
VarDeclType.CONST -> throw VmExecutionException("can't be const")
VarDeclType.STRUCT -> TODO("struct decltype")
}
}
stmt.target.memoryAddress != null -> {

View File

@ -101,7 +101,7 @@ fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue {
if(variable is VarDecl) {
if(variable.type==VarDeclType.VAR)
return ctx.runtimeVars.get(variable.definingScope(), variable.name)
else if(variable.type==VarDeclType.STRUCT) {
else if(variable.datatype==DataType.STRUCT) {
throw VmExecutionException("cannot process structs by-value. at ${expr.position}")
}
else {

View File

@ -49,9 +49,6 @@ class VariablesCreator(private val runtimeVariables: RuntimeVariables, private v
VarDeclType.CONST -> {
// consts should have been const-folded away
}
VarDeclType.STRUCT -> {
// struct vardecl can be skipped because its members have been flattened out
}
}
}
return super.visit(decl)

View File

@ -7,47 +7,47 @@
uword derp
ubyte[] v = [22,33,44]
Color foreground
Color foreground2 = [11,22,33]
Color foreground = [1,2,3]
c64scr.print_ub(foreground.red)
c64.CHROUT(':')
c64scr.print_ub(foreground.green)
c64.CHROUT(':')
c64scr.print_ub(foreground.blue)
c64.CHROUT('\n')
foreground.red = 111
; foreground2.red = 111
Color background = [255,255,255] ; @todo make zeros if no value is given
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
}
; 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