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 { private fun prog8Parser.VardeclContext.toAst(): VarDecl {
return VarDecl( return VarDecl(
if(structname!=null) VarDeclType.STRUCT else VarDeclType.VAR, VarDeclType.VAR,
datatype()?.toAst() ?: DataType.STRUCT, datatype()?.toAst() ?: DataType.STRUCT,
ZEROPAGE() != null, ZEROPAGE() != null,
arrayindex()?.toAst(), arrayindex()?.toAst(),

View File

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

View File

@ -6,7 +6,7 @@ import prog8.ast.processing.*
import prog8.ast.statements.Assignment import prog8.ast.statements.Assignment
import prog8.ast.statements.ForLoop import prog8.ast.statements.ForLoop
import prog8.compiler.CompilationOptions 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 // 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 const val autoHeapValuePrefix = "auto_heap_value_"
internal fun Program.removeNops() { internal fun Program.removeNopsFlattenAnonScopes() {
val remover = RemoveNops() val flattener = FlattenAnonymousScopesAndRemoveNops()
remover.visit(this) flattener.visit(this)
} }

View File

@ -489,6 +489,14 @@ internal class AstChecker(private val program: Program,
when(decl.type) { when(decl.type) {
VarDeclType.VAR, VarDeclType.CONST -> { 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) { if (decl.value == null) {
when { when {
decl.datatype in NumericDatatypes -> { decl.datatype in NumericDatatypes -> {
@ -502,6 +510,9 @@ internal class AstChecker(private val program: Program,
litVal.parent = decl litVal.parent = decl
decl.value = litVal 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 -> { decl.type== VarDeclType.VAR -> {
val litVal = LiteralValue(decl.datatype, initHeapId = heapStringSentinel, position = decl.position) // point to the sentinel heap value instead val litVal = LiteralValue(decl.datatype, initHeapId = heapStringSentinel, position = decl.position) // point to the sentinel heap value instead
litVal.parent=decl 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) return super.visit(decl)
@ -1274,7 +1275,7 @@ internal class AstChecker(private val program: Program,
else { else {
if(decl.zeropage) if(decl.zeropage)
checkResult.add(SyntaxError("struct can not contain zeropage members", decl.position)) 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)) 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, // is it a struct variable? then define all its struct members as mangled names,
// and include the original decl as well. // and include the original decl as well.
if(decl.type==VarDeclType.STRUCT) { if(decl.datatype==DataType.STRUCT) {
if(decl.structHasBeenFlattened) if(decl.structHasBeenFlattened)
return decl // don't do this multiple times 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.IdentifierReference
import prog8.ast.expressions.LiteralValue import prog8.ast.expressions.LiteralValue
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.compiler.CompilerException
internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope): IAstModifyingVisitor { internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope): IAstModifyingVisitor {
// For VarDecls that declare an initialization value: // For VarDecls that declare an initialization value:
@ -59,6 +61,24 @@ internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope
decl.position 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 return decl
} }

View File

@ -103,16 +103,10 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
} }
override fun visit(decl: VarDecl) { override fun visit(decl: VarDecl) {
// if(decl.hiddenButDoNotRemove) {
// // skip autogenerated vardecl
// return
// }
when(decl.type) { when(decl.type) {
VarDeclType.VAR -> {} VarDeclType.VAR -> {}
VarDeclType.CONST -> output("const ") VarDeclType.CONST -> output("const ")
VarDeclType.MEMORY -> output("&") VarDeclType.MEMORY -> output("&")
VarDeclType.STRUCT -> output("${decl.struct!!.name} ")
} }
output(decl.struct?.name ?: "") output(decl.struct?.name ?: "")
output(datatypeString(decl.datatype)) output(datatypeString(decl.datatype))
@ -185,7 +179,7 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
private fun outputStatements(statements: List<IStatement>) { private fun outputStatements(statements: List<IStatement>) {
for(stmt in statements) { for(stmt in statements) {
if(stmt is VarDecl && stmt.hiddenButDoNotRemove) if(stmt is VarDecl && stmt.hiddenButDoNotRemove)
continue // skip autogenerated decls continue // skip autogenerated decls (to avoid generating a newline)
outputi("") outputi("")
stmt.accept(this) stmt.accept(this)
output("\n") output("\n")
@ -436,6 +430,6 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
outputln("") outputln("")
} }
override fun visit(nopStatement: NopStatement) { 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") 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 -> { DataType.STRUCT -> {
// Assume the value is an array. Flatten the struct assignment into memberwise assignments. // Assume the value is an array. Flatten the struct assignment into memberwise assignments.
val identifier = stmt.target.identifier!! flattenStructAssignment(stmt, program).forEach { translate(it) }
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 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")
@ -1481,6 +1464,26 @@ internal class Compiler(private val program: Program) {
popValueIntoTarget(stmt.target, datatype) 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) { private fun pushHeapVarAddress(value: IExpression, removeLastOpcode: Boolean) {
when (value) { when (value) {
is LiteralValue -> throw CompilerException("can only push address of string or array (value on the heap)") 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)) prog.instr(opcode, RuntimeValue(DataType.UWORD, address))
} }
VarDeclType.CONST -> throw CompilerException("cannot assign to const") VarDeclType.CONST -> throw CompilerException("cannot assign to const")
VarDeclType.STRUCT -> {
TODO("decltype struct $assignTarget")
}
} }
} else throw CompilerException("invalid assignment target type ${target::class}") } 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.checkValid(compilerOptions) // check if final tree is valid
programAst.checkRecursion() // check if there are recursive subroutine calls 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") throw CompilerException("array should already be in the heap")
RuntimeValue(decl.datatype, heapId = litval.heapId) RuntimeValue(decl.datatype, heapId = litval.heapId)
} }
DataType.STRUCT -> {
// struct variables have been flattened already
return
}
else -> throw CompilerException("weird datatype") else -> throw CompilerException("weird datatype")
} }
currentBlock.variables[scopedname] = value currentBlock.variables[scopedname] = value
@ -435,9 +439,6 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
if(lv.type in IntegerDatatypes) if(lv.type in IntegerDatatypes)
currentBlock.memoryPointers[scopedname] = Pair(lv.asIntegerValue!!, decl.datatype) 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 { internal fun Program.optimizeStatements(optimizeInlining: Boolean): Int {
val optimizer = StatementOptimizer(this, optimizeInlining) val optimizer = StatementOptimizer(this, optimizeInlining)
optimizer.visit(this) 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 modules.forEach { it.linkParents(this.namespace) } // re-link in final configuration
return optimizer.optimizationsDone 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 { internal class StatementOptimizer(private val program: Program, private val optimizeInlining: Boolean) : IAstModifyingVisitor {
var optimizationsDone: Int = 0 var optimizationsDone: Int = 0
private set private set
var scopesToFlatten = mutableListOf<INameScope>()
private val pureBuiltinFunctions = BuiltinFunctions.filter { it.value.pure } private val pureBuiltinFunctions = BuiltinFunctions.filter { it.value.pure }
private val callgraph = CallGraph(program) private val callgraph = CallGraph(program)
@ -621,11 +620,6 @@ internal class StatementOptimizer(private val program: Program, private val opti
if(linesToRemove.isNotEmpty()) { if(linesToRemove.isNotEmpty()) {
linesToRemove.reversed().forEach{scope.statements.removeAt(it)} 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) return super.visit(scope)
} }
@ -642,17 +636,38 @@ internal class StatementOptimizer(private val program: Program, private val opti
internal class RemoveNops: IAstVisitor { internal class FlattenAnonymousScopesAndRemoveNops: IAstVisitor {
val nopStatements = mutableListOf<NopStatement>() private var scopesToFlatten = mutableListOf<INameScope>()
private val nopStatements = mutableListOf<NopStatement>()
override fun visit(program: Program) { override fun visit(program: Program) {
super.visit(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 { this.nopStatements.forEach {
it.definingScope().remove(it) 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) { override fun visit(nopStatement: NopStatement) {
nopStatements.add(nopStatement) nopStatements.add(nopStatement)
} }

View File

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

View File

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

View File

@ -49,9 +49,6 @@ class VariablesCreator(private val runtimeVariables: RuntimeVariables, private v
VarDeclType.CONST -> { VarDeclType.CONST -> {
// consts should have been const-folded away // 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) return super.visit(decl)

View File

@ -7,47 +7,47 @@
uword derp uword derp
ubyte[] v = [22,33,44] ubyte[] v = [22,33,44]
Color foreground Color foreground = [1,2,3]
Color foreground2 = [11,22,33] 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 { struct Color {
ubyte red ubyte red
ubyte green ubyte green