mirror of
https://github.com/irmen/prog8.git
synced 2024-11-29 17:50:35 +00:00
struct fixes
This commit is contained in:
parent
9f6fa60bf1
commit
717b5f3b07
@ -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(),
|
||||
|
@ -94,8 +94,7 @@ enum class BranchCondition {
|
||||
enum class VarDeclType {
|
||||
VAR,
|
||||
CONST,
|
||||
MEMORY,
|
||||
STRUCT
|
||||
MEMORY
|
||||
}
|
||||
|
||||
val IterableDatatypes = setOf(
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
@ -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}")
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 -> {
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -7,46 +7,46 @@
|
||||
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
|
||||
}
|
||||
|
||||
; 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
|
||||
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
|
||||
; }
|
||||
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user