From 9e6408244f712c35aaaf24e226f12ac3b47af57b Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 9 Jul 2019 19:44:59 +0200 Subject: [PATCH] fix scoping of variables in when statement --- compiler/src/prog8/ast/Interfaces.kt | 4 ++ compiler/src/prog8/ast/base/Extensions.kt | 1 + .../prog8/ast/expressions/AstExpressions.kt | 3 +- .../ast/processing/AstIdentifiersChecker.kt | 1 + .../VarInitValueAndAddressOfCreator.kt | 1 - compiler/src/prog8/compiler/Compiler.kt | 6 +- .../intermediate/IntermediateProgram.kt | 72 +++++++++++-------- .../prog8/compiler/target/c64/SimpleAsm.kt | 22 +++++- compiler/src/prog8/vm/stackvm/StackVm.kt | 22 +++--- examples/test.p8 | 43 +++++------ 10 files changed, 106 insertions(+), 69 deletions(-) diff --git a/compiler/src/prog8/ast/Interfaces.kt b/compiler/src/prog8/ast/Interfaces.kt index cb6405519..51d7967ac 100644 --- a/compiler/src/prog8/ast/Interfaces.kt +++ b/compiler/src/prog8/ast/Interfaces.kt @@ -80,6 +80,7 @@ interface INameScope { val subscopes = mutableMapOf() for(stmt in statements) { when(stmt) { + // NOTE: if other nodes are introduced that are a scope of contain subscopes, they must be added here! is INameScope -> subscopes[stmt.name] = stmt is ForLoop -> subscopes[stmt.body.name] = stmt.body is RepeatLoop -> subscopes[stmt.body.name] = stmt.body @@ -94,6 +95,9 @@ interface INameScope { if(stmt.elsepart.containsCodeOrVars()) subscopes[stmt.elsepart.name] = stmt.elsepart } + is WhenStatement -> { + stmt.choices.forEach { subscopes[it.statements.name] = it.statements } + } } } return subscopes diff --git a/compiler/src/prog8/ast/base/Extensions.kt b/compiler/src/prog8/ast/base/Extensions.kt index c3bf4eb24..236fe2e83 100644 --- a/compiler/src/prog8/ast/base/Extensions.kt +++ b/compiler/src/prog8/ast/base/Extensions.kt @@ -54,6 +54,7 @@ internal fun Program.checkIdentifiers() { // add any anonymous variables for heap values that are used, // and replace an iterable literalvalue by identifierref to new local variable + // TODO: this is't doing anything anymore? for (variable in checker.anonymousVariablesFromHeap.values) { val scope = variable.first.definingScope() scope.statements.add(variable.second) diff --git a/compiler/src/prog8/ast/expressions/AstExpressions.kt b/compiler/src/prog8/ast/expressions/AstExpressions.kt index 8b6764634..7248b4418 100644 --- a/compiler/src/prog8/ast/expressions/AstExpressions.kt +++ b/compiler/src/prog8/ast/expressions/AstExpressions.kt @@ -1,6 +1,7 @@ package prog8.ast.expressions import prog8.ast.* +import prog8.ast.antlr.escape import prog8.ast.base.* import prog8.ast.processing.IAstModifyingVisitor import prog8.ast.processing.IAstVisitor @@ -424,7 +425,7 @@ open class LiteralValue(val type: DataType, DataType.UWORD -> "uword:$wordvalue" DataType.WORD -> "word:$wordvalue" DataType.FLOAT -> "float:$floatvalue" - in StringDatatypes -> "str:$strvalue" + in StringDatatypes -> "str:'${escape(strvalue?:"")}'" in ArrayDatatypes -> "array:$arrayvalue" else -> throw FatalAstException("weird datatype") } diff --git a/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt b/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt index 78205bbb3..3e5551f13 100644 --- a/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt +++ b/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt @@ -199,6 +199,7 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo if(literalValue.heapId!=null && literalValue.parent !is VarDecl) { // a literal value that's not declared as a variable, which refers to something on the heap. // we need to introduce an auto-generated variable for this to be able to refer to the value! + // (note: ususally, this has been taken care of already when the var was created) val variable = VarDecl(VarDeclType.VAR, literalValue.type, false, null, "$autoHeapValuePrefix${literalValue.heapId}", literalValue, isArray = false, autoGenerated = false, position = literalValue.position) anonymousVariablesFromHeap[variable.name] = Pair(literalValue, variable) diff --git a/compiler/src/prog8/ast/processing/VarInitValueAndAddressOfCreator.kt b/compiler/src/prog8/ast/processing/VarInitValueAndAddressOfCreator.kt index cf05c90de..c76ef5aff 100644 --- a/compiler/src/prog8/ast/processing/VarInitValueAndAddressOfCreator.kt +++ b/compiler/src/prog8/ast/processing/VarInitValueAndAddressOfCreator.kt @@ -110,7 +110,6 @@ internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope val variable = VarDecl(VarDeclType.VAR, strvalue.type, false, null, autoVarName, strvalue, isArray = false, autoGenerated = false, position = strvalue.position) addVarDecl(strvalue.definingScope(), variable) - // println("MADE ANONVAR $variable") // XXX } } } diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index c5e528cd5..99a9584b5 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -2106,13 +2106,11 @@ internal class Compiler(private val program: Program) { previousValue = choiceVal if (conditionDt in ByteDatatypes) { prog.instr(Opcode.DUP_B) - prog.instr(Opcode.PUSH_BYTE, RuntimeValue(conditionDt!!, subtract)) - prog.instr(opcodeCompare(conditionDt)) + prog.instr(opcodeCompare(conditionDt!!), RuntimeValue(conditionDt, subtract)) } else { prog.instr(Opcode.DUP_W) - prog.instr(Opcode.PUSH_WORD, RuntimeValue(conditionDt!!, subtract)) - prog.instr(opcodeCompare(conditionDt)) + prog.instr(opcodeCompare(conditionDt!!), RuntimeValue(conditionDt, subtract)) } val choiceLabel = makeLabel(whenstmt, "choice_$choiceVal") choiceLabels.add(choiceLabel) diff --git a/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt b/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt index 953e288fd..2c424942e 100644 --- a/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt +++ b/compiler/src/prog8/compiler/intermediate/IntermediateProgram.kt @@ -459,10 +459,14 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap fun writeCode(out: PrintStream, embeddedLabels: Boolean=true) { out.println("; stackVM program code for '$name'") - out.println("%memory") - if(memory.isNotEmpty()) - TODO("add support for writing/reading initial memory values") - out.println("%end_memory") + writeMemory(out) + writeHeap(out) + for(blk in blocks) { + writeBlock(out, blk, embeddedLabels) + } + } + + private fun writeHeap(out: PrintStream) { out.println("%heap") heap.allEntries().forEach { out.print("${it.key} ${it.value.type.name.toLowerCase()} ") @@ -491,34 +495,42 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap } } out.println("%end_heap") - for(blk in blocks) { - out.println("\n%block ${blk.name} ${blk.address?.toString(16) ?: ""}") + } - out.println("%variables") - for(variable in blk.variables) { - val valuestr = variable.value.toString() - out.println("${variable.key} ${variable.value.type.name.toLowerCase()} $valuestr") - } - out.println("%end_variables") - out.println("%memorypointers") - for(iconst in blk.memoryPointers) { - out.println("${iconst.key} ${iconst.value.second.name.toLowerCase()} uw:${iconst.value.first.toString(16)}") - } - out.println("%end_memorypointers") - out.println("%instructions") - val labels = blk.labels.entries.associateBy({it.value}) {it.key} - for(instr in blk.instructions) { - if(!embeddedLabels) { - val label = labels[instr] - if (label != null) - out.println("$label:") - } else { - out.println(instr) - } - } - out.println("%end_instructions") + private fun writeBlock(out: PrintStream, blk: ProgramBlock, embeddedLabels: Boolean) { + out.println("\n%block ${blk.name} ${blk.address?.toString(16) ?: ""}") - out.println("%end_block") + out.println("%variables") + for (variable in blk.variables) { + val valuestr = variable.value.toString() + out.println("${variable.key} ${variable.value.type.name.toLowerCase()} $valuestr") } + out.println("%end_variables") + out.println("%memorypointers") + for (iconst in blk.memoryPointers) { + out.println("${iconst.key} ${iconst.value.second.name.toLowerCase()} uw:${iconst.value.first.toString(16)}") + } + out.println("%end_memorypointers") + out.println("%instructions") + val labels = blk.labels.entries.associateBy({ it.value }) { it.key } + for (instr in blk.instructions) { + if (!embeddedLabels) { + val label = labels[instr] + if (label != null) + out.println("$label:") + } else { + out.println(instr) + } + } + out.println("%end_instructions") + + out.println("%end_block") + } + + private fun writeMemory(out: PrintStream) { + out.println("%memory") + if (memory.isNotEmpty()) + TODO("add support for writing/reading initial memory values") + out.println("%end_memory") } } diff --git a/compiler/src/prog8/compiler/target/c64/SimpleAsm.kt b/compiler/src/prog8/compiler/target/c64/SimpleAsm.kt index e6e058c99..d1e9b3f1e 100644 --- a/compiler/src/prog8/compiler/target/c64/SimpleAsm.kt +++ b/compiler/src/prog8/compiler/target/c64/SimpleAsm.kt @@ -61,13 +61,33 @@ internal fun simpleInstr2Asm(ins: Instruction, block: IntermediateProgram.Progra Opcode.RRESTOREX -> " sta ${C64Zeropage.SCRATCH_REG} | pla | tax | lda ${C64Zeropage.SCRATCH_REG}" Opcode.DISCARD_BYTE -> " inx" Opcode.DISCARD_WORD -> " inx" + Opcode.DISCARD_FLOAT -> " inx | inx | inx" Opcode.DUP_B -> { " dex | lda ${(ESTACK_LO+1).toHex()},x | sta ${ESTACK_LO.toHex()},x" } Opcode.DUP_W -> { " dex | lda ${(ESTACK_LO+1).toHex()},x | sta ${ESTACK_LO.toHex()},x | lda ${(ESTACK_HI+1).toHex()},x | sta ${ESTACK_HI.toHex()},x " } - Opcode.DISCARD_FLOAT -> " inx | inx | inx" + + Opcode.CMP_B, Opcode.CMP_UB -> { + " inx | lda ${ESTACK_LO.toHex()},x | inx | cmp ${ESTACK_LO.toHex()},x " + } + + Opcode.CMP_W, Opcode.CMP_UW -> { + """ + inx + inx + lda ${(ESTACK_HI-1).toHex()},x + cmp ${(ESTACK_HI).toHex()},x + bne + + lda ${(ESTACK_LO-1).toHex()},x + cmp ${(ESTACK_LO).toHex()},x + bne + + lda #0 ++ + """ + } + Opcode.INLINE_ASSEMBLY -> "@inline@" + (ins.callLabel2 ?: "") // All of the inline assembly is stored in the calllabel2 property. the '@inline@' is a special marker to accept it. Opcode.INCLUDE_FILE -> { val offset = if(ins.arg==null) "" else ", ${ins.arg.integerValue()}" diff --git a/compiler/src/prog8/vm/stackvm/StackVm.kt b/compiler/src/prog8/vm/stackvm/StackVm.kt index e02d9d8de..04ec2383e 100644 --- a/compiler/src/prog8/vm/stackvm/StackVm.kt +++ b/compiler/src/prog8/vm/stackvm/StackVm.kt @@ -20,7 +20,7 @@ import java.util.* import kotlin.math.* -enum class Syscall(val callNr: Short) { +internal enum class Syscall(val callNr: Short) { VM_WRITE_MEMCHR(10), // print a single char from the memory address popped from stack VM_WRITE_MEMSTR(11), // print a 0-terminated petscii string from the memory address popped from stack VM_WRITE_NUM(12), // pop from the evaluation stack and print it as a number @@ -107,10 +107,9 @@ enum class Syscall(val callNr: Short) { SYSASM_c64flt_print_f(214), } +internal val syscallNames = enumValues().map { it.name }.toSet() -val syscallNames = enumValues().map { it.name }.toSet() - -val syscallsForStackVm = setOf( +internal val syscallsForStackVm = setOf( Syscall.VM_WRITE_MEMCHR, Syscall.VM_WRITE_MEMSTR, Syscall.VM_WRITE_NUM, @@ -123,13 +122,13 @@ val syscallsForStackVm = setOf( Syscall.VM_GFX_LINE ) -class VmExecutionException(msg: String?) : Exception(msg) +internal class VmExecutionException(msg: String?) : Exception(msg) -class VmTerminationException(msg: String?) : Exception(msg) +internal class VmTerminationException(msg: String?) : Exception(msg) -class VmBreakpointException : Exception("breakpoint") +internal class VmBreakpointException : Exception("breakpoint") -class MyStack : Stack() { +internal class MyStack : Stack() { fun peek(amount: Int) : List { return this.toList().subList(max(0, size-amount), size) } @@ -141,7 +140,6 @@ class MyStack : Stack() { } } - class StackVm(private var traceOutputFile: String?) { val mem = Memory(::memread, ::memwrite) var P_carry: Boolean = false @@ -156,9 +154,9 @@ class StackVm(private var traceOutputFile: String?) { private set var memoryPointers = mutableMapOf>() // all named pointers private set - var evalstack = MyStack() + internal var evalstack = MyStack() private set - var callstack = MyStack() + internal var callstack = MyStack() private set private var program = listOf() private var labels = emptyMap() @@ -204,7 +202,7 @@ class StackVm(private var traceOutputFile: String?) { throw VmExecutionException("program contains variable(s) for the reserved registers A/X/Y") // define the 'registers' variables["A"] = RuntimeValue(DataType.UBYTE, 0) - variables["X"] = RuntimeValue(DataType.UBYTE, 0) + variables["X"] = RuntimeValue(DataType.UBYTE, 255) variables["Y"] = RuntimeValue(DataType.UBYTE, 0) initMemory(program.memory) diff --git a/examples/test.p8 b/examples/test.p8 index 49a371c67..3a76bfd17 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,33 +1,34 @@ %import c64utils %zeropage basicsafe -%option enable_floats ~ main { sub start() { - A=10 + A=100 Y=22 - uword uw = A*Y + uword uw = (A as uword)*Y - str teststring = "hello" - c64scr.print(&teststring) + c64scr.print("stack (255?): ") + c64scr.print_ub(X) + c64.CHROUT('\n') + c64scr.print_uw(uw) + c64scr.print("?: ") when uw { - 12345 -> { - A=44 - } - 12346 -> { - A=44 - } - 12347 -> { - A=44 - } - else -> { - A=0 - } + 12345 -> c64scr.print("12345") + 12346 -> c64scr.print("12346") + 2200 -> c64scr.print("2200") + 12347 -> c64scr.print("12347") + else -> c64scr.print("else") } + c64.CHROUT('\n') - when 4+A+Y { + A=30 + Y=2 + + c64scr.print_ub(A+Y) + c64scr.print("?: ") + when A+Y { 10 -> { c64scr.print("ten") } @@ -48,9 +49,11 @@ } else -> { c64scr.print("!??!\n") - c64scr.print("!??!!??!\n") - c64scr.print("!??!!??!!?!\n") } } + c64.CHROUT('\n') + + c64scr.print("stack (255?): ") + c64scr.print_ub(X) } }