mirror of
https://github.com/irmen/prog8.git
synced 2025-02-18 05:30:34 +00:00
intermediate program written in blocks
This commit is contained in:
parent
987915a77a
commit
dff4518608
@ -31,3 +31,11 @@ sub start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
~ block2 $c000 {
|
||||||
|
|
||||||
|
str derp="hello"
|
||||||
|
byte v =44
|
||||||
|
return
|
||||||
|
}
|
||||||
|
@ -99,7 +99,7 @@ fun main(args: Array<String>) {
|
|||||||
|
|
||||||
val stackVmFilename = intermediate.name + "_stackvm.txt"
|
val stackVmFilename = intermediate.name + "_stackvm.txt"
|
||||||
val stackvmFile = PrintStream(File(stackVmFilename), "utf-8")
|
val stackvmFile = PrintStream(File(stackVmFilename), "utf-8")
|
||||||
intermediate.writeAsText(stackvmFile)
|
intermediate.writeCode(stackvmFile)
|
||||||
stackvmFile.close()
|
stackvmFile.close()
|
||||||
println("StackVM program code written to '$stackVmFilename'")
|
println("StackVM program code written to '$stackVmFilename'")
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
323
compiler/src/prog8/compiler/IntermediateProgram.kt
Normal file
323
compiler/src/prog8/compiler/IntermediateProgram.kt
Normal file
@ -0,0 +1,323 @@
|
|||||||
|
package prog8.compiler
|
||||||
|
|
||||||
|
import prog8.ast.DataType
|
||||||
|
import prog8.ast.LiteralValue
|
||||||
|
import prog8.ast.Position
|
||||||
|
import prog8.ast.VarDecl
|
||||||
|
import prog8.stackvm.*
|
||||||
|
import java.io.PrintStream
|
||||||
|
|
||||||
|
|
||||||
|
class IntermediateProgram(val name: String, val heap: HeapValues) {
|
||||||
|
|
||||||
|
private class ProgramBlock(val scopedname: String, val address: Int?) {
|
||||||
|
val instructions = mutableListOf<Instruction>()
|
||||||
|
val variables = mutableMapOf<String, Value>()
|
||||||
|
val labels = mutableMapOf<String, Instruction>()
|
||||||
|
|
||||||
|
val numVariables: Int
|
||||||
|
get() { return variables.size }
|
||||||
|
val numInstructions: Int
|
||||||
|
get() { return instructions.filter { it.opcode!= Opcode.LINE }.size }
|
||||||
|
}
|
||||||
|
|
||||||
|
private val blocks = mutableListOf<ProgramBlock>()
|
||||||
|
private val memory = mutableMapOf<Int, List<Value>>()
|
||||||
|
private lateinit var currentBlock: ProgramBlock
|
||||||
|
|
||||||
|
val numVariables: Int
|
||||||
|
get() = blocks.sumBy { it.numVariables }
|
||||||
|
val numInstructions: Int
|
||||||
|
get() = blocks.sumBy { it.numInstructions }
|
||||||
|
|
||||||
|
fun optimize() {
|
||||||
|
println("Optimizing stackVM code...")
|
||||||
|
optimizeDataConversionAndUselessDiscards()
|
||||||
|
optimizeVariableCopying()
|
||||||
|
optimizeMultipleSequentialLineInstrs()
|
||||||
|
// todo optimize stackvm code more
|
||||||
|
|
||||||
|
// remove nops (that are not a label)
|
||||||
|
for (blk in blocks) {
|
||||||
|
blk.instructions.removeIf { it.opcode== Opcode.NOP && it !is LabelInstr }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun optimizeMultipleSequentialLineInstrs() {
|
||||||
|
for(blk in blocks) {
|
||||||
|
val instructionsToReplace = mutableMapOf<Int, Instruction>()
|
||||||
|
|
||||||
|
blk.instructions.asSequence().withIndex().windowed(2).toList().forEach {
|
||||||
|
if (it[0].value.opcode == Opcode.LINE && it[1].value.opcode == Opcode.LINE)
|
||||||
|
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (rins in instructionsToReplace) {
|
||||||
|
blk.instructions[rins.key] = rins.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun optimizeVariableCopying() {
|
||||||
|
for(blk in blocks) {
|
||||||
|
|
||||||
|
val instructionsToReplace = mutableMapOf<Int, Instruction>()
|
||||||
|
|
||||||
|
blk.instructions.asSequence().withIndex().windowed(2).toList().forEach {
|
||||||
|
when (it[0].value.opcode) {
|
||||||
|
Opcode.PUSH_VAR_BYTE ->
|
||||||
|
if (it[1].value.opcode == Opcode.POP_VAR_BYTE) {
|
||||||
|
if (it[0].value.callLabel != it[1].value.callLabel)
|
||||||
|
instructionsToReplace[it[0].index] = Instruction(Opcode.COPY_VAR_BYTE, null, it[0].value.callLabel, it[1].value.callLabel)
|
||||||
|
else
|
||||||
|
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
|
||||||
|
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
|
||||||
|
}
|
||||||
|
Opcode.PUSH_VAR_WORD ->
|
||||||
|
if (it[1].value.opcode == Opcode.POP_VAR_WORD) {
|
||||||
|
if (it[0].value.callLabel != it[1].value.callLabel)
|
||||||
|
instructionsToReplace[it[0].index] = Instruction(Opcode.COPY_VAR_WORD, null, it[0].value.callLabel, it[1].value.callLabel)
|
||||||
|
else
|
||||||
|
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
|
||||||
|
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
|
||||||
|
}
|
||||||
|
Opcode.PUSH_VAR_FLOAT ->
|
||||||
|
if (it[1].value.opcode == Opcode.POP_VAR_FLOAT) {
|
||||||
|
if (it[0].value.callLabel != it[1].value.callLabel)
|
||||||
|
instructionsToReplace[it[0].index] = Instruction(Opcode.COPY_VAR_FLOAT, null, it[0].value.callLabel, it[1].value.callLabel)
|
||||||
|
else
|
||||||
|
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
|
||||||
|
instructionsToReplace[it[1].index] = Instruction(Opcode.NOP)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (rins in instructionsToReplace) {
|
||||||
|
blk.instructions[rins.key] = rins.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun optimizeDataConversionAndUselessDiscards() {
|
||||||
|
// - push value followed by a data type conversion -> push the value in the correct type and remove the conversion
|
||||||
|
// - push something followed by a discard -> remove both
|
||||||
|
val instructionsToReplace = mutableMapOf<Int, Instruction>()
|
||||||
|
|
||||||
|
fun optimizeDiscardAfterPush(index0: Int, index1: Int, ins1: Instruction) {
|
||||||
|
if (ins1.opcode == Opcode.DISCARD_FLOAT || ins1.opcode == Opcode.DISCARD_WORD || ins1.opcode == Opcode.DISCARD_BYTE) {
|
||||||
|
instructionsToReplace[index0] = Instruction(Opcode.NOP)
|
||||||
|
instructionsToReplace[index1] = Instruction(Opcode.NOP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun optimizeFloatConversion(index0: Int, index1: Int, ins1: Instruction) {
|
||||||
|
when (ins1.opcode) {
|
||||||
|
Opcode.LSB,
|
||||||
|
Opcode.MSB,
|
||||||
|
Opcode.B2WORD,
|
||||||
|
Opcode.UB2UWORD,
|
||||||
|
Opcode.MSB2WORD,
|
||||||
|
Opcode.B2FLOAT,
|
||||||
|
Opcode.UB2FLOAT,
|
||||||
|
Opcode.UW2FLOAT,
|
||||||
|
Opcode.W2FLOAT -> throw CompilerException("invalid conversion following a float")
|
||||||
|
Opcode.DISCARD_FLOAT -> {
|
||||||
|
instructionsToReplace[index0] = Instruction(Opcode.NOP)
|
||||||
|
instructionsToReplace[index1] = Instruction(Opcode.NOP)
|
||||||
|
}
|
||||||
|
Opcode.DISCARD_BYTE, Opcode.DISCARD_WORD -> throw CompilerException("invalid discard type following a float")
|
||||||
|
else -> throw CompilerException("invalid conversion opcode ${ins1.opcode}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun optimizeWordConversion(index0: Int, ins0: Instruction, index1: Int, ins1: Instruction) {
|
||||||
|
when (ins1.opcode) {
|
||||||
|
Opcode.LSB -> {
|
||||||
|
val ins = Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, ins0.arg!!.integerValue() and 255))
|
||||||
|
instructionsToReplace[index0] = ins
|
||||||
|
instructionsToReplace[index1] = Instruction(Opcode.NOP)
|
||||||
|
}
|
||||||
|
Opcode.MSB -> {
|
||||||
|
val ins = Instruction(Opcode.PUSH_BYTE, Value(DataType.UBYTE, ins0.arg!!.integerValue() ushr 8 and 255))
|
||||||
|
instructionsToReplace[index0] = ins
|
||||||
|
instructionsToReplace[index1] = Instruction(Opcode.NOP)
|
||||||
|
}
|
||||||
|
Opcode.B2WORD,
|
||||||
|
Opcode.UB2UWORD,
|
||||||
|
Opcode.MSB2WORD,
|
||||||
|
Opcode.B2FLOAT,
|
||||||
|
Opcode.UB2FLOAT -> throw CompilerException("invalid conversion following a word")
|
||||||
|
Opcode.W2FLOAT, Opcode.UW2FLOAT -> {
|
||||||
|
val ins = Instruction(Opcode.PUSH_FLOAT, Value(DataType.FLOAT, ins0.arg!!.integerValue().toDouble()))
|
||||||
|
instructionsToReplace[index0] = ins
|
||||||
|
instructionsToReplace[index1] = Instruction(Opcode.NOP)
|
||||||
|
}
|
||||||
|
Opcode.DISCARD_WORD -> {
|
||||||
|
instructionsToReplace[index0] = Instruction(Opcode.NOP)
|
||||||
|
instructionsToReplace[index1] = Instruction(Opcode.NOP)
|
||||||
|
}
|
||||||
|
Opcode.DISCARD_BYTE, Opcode.DISCARD_FLOAT -> throw CompilerException("invalid discard type following a byte")
|
||||||
|
else -> throw CompilerException("invalid conversion opcode ${ins1.opcode}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun optimizeByteConversion(index0: Int, ins0: Instruction, index1: Int, ins1: Instruction) {
|
||||||
|
when (ins1.opcode) {
|
||||||
|
Opcode.LSB -> instructionsToReplace[index1] = Instruction(Opcode.NOP)
|
||||||
|
Opcode.MSB -> throw CompilerException("msb of a byte")
|
||||||
|
Opcode.UB2UWORD -> {
|
||||||
|
val ins = Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, ins0.arg!!.integerValue()))
|
||||||
|
instructionsToReplace[index0] = ins
|
||||||
|
instructionsToReplace[index1] = Instruction(Opcode.NOP)
|
||||||
|
}
|
||||||
|
Opcode.B2WORD -> {
|
||||||
|
val ins = Instruction(Opcode.PUSH_WORD, Value(DataType.WORD, ins0.arg!!.integerValue()))
|
||||||
|
instructionsToReplace[index0] = ins
|
||||||
|
instructionsToReplace[index1] = Instruction(Opcode.NOP)
|
||||||
|
}
|
||||||
|
Opcode.MSB2WORD -> {
|
||||||
|
val ins = Instruction(Opcode.PUSH_WORD, Value(DataType.UWORD, 256 * ins0.arg!!.integerValue()))
|
||||||
|
instructionsToReplace[index0] = ins
|
||||||
|
instructionsToReplace[index1] = Instruction(Opcode.NOP)
|
||||||
|
}
|
||||||
|
Opcode.B2FLOAT, Opcode.UB2FLOAT -> {
|
||||||
|
val ins = Instruction(Opcode.PUSH_FLOAT, Value(DataType.FLOAT, ins0.arg!!.integerValue().toDouble()))
|
||||||
|
instructionsToReplace[index0] = ins
|
||||||
|
instructionsToReplace[index1] = Instruction(Opcode.NOP)
|
||||||
|
}
|
||||||
|
Opcode.W2FLOAT, Opcode.UW2FLOAT -> throw CompilerException("invalid conversion following a byte")
|
||||||
|
Opcode.DISCARD_BYTE -> {
|
||||||
|
instructionsToReplace[index0] = Instruction(Opcode.NOP)
|
||||||
|
instructionsToReplace[index1] = Instruction(Opcode.NOP)
|
||||||
|
}
|
||||||
|
Opcode.DISCARD_WORD, Opcode.DISCARD_FLOAT -> throw CompilerException("invalid discard type following a byte")
|
||||||
|
else -> throw CompilerException("invalid conversion opcode ${ins1.opcode}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(blk in blocks) {
|
||||||
|
instructionsToReplace.clear()
|
||||||
|
|
||||||
|
val typeConversionOpcodes = setOf(
|
||||||
|
Opcode.LSB,
|
||||||
|
Opcode.MSB,
|
||||||
|
Opcode.B2WORD,
|
||||||
|
Opcode.UB2UWORD,
|
||||||
|
Opcode.MSB2WORD,
|
||||||
|
Opcode.B2FLOAT,
|
||||||
|
Opcode.UB2FLOAT,
|
||||||
|
Opcode.W2FLOAT,
|
||||||
|
Opcode.UW2FLOAT,
|
||||||
|
Opcode.DISCARD_BYTE,
|
||||||
|
Opcode.DISCARD_WORD,
|
||||||
|
Opcode.DISCARD_FLOAT
|
||||||
|
)
|
||||||
|
blk.instructions.asSequence().withIndex().windowed(2).toList().forEach {
|
||||||
|
if (it[1].value.opcode in typeConversionOpcodes) {
|
||||||
|
when (it[0].value.opcode) {
|
||||||
|
Opcode.PUSH_BYTE -> optimizeByteConversion(it[0].index, it[0].value, it[1].index, it[1].value)
|
||||||
|
Opcode.PUSH_WORD -> optimizeWordConversion(it[0].index, it[0].value, it[1].index, it[1].value)
|
||||||
|
Opcode.PUSH_FLOAT -> optimizeFloatConversion(it[0].index, it[1].index, it[1].value)
|
||||||
|
Opcode.PUSH_VAR_FLOAT,
|
||||||
|
Opcode.PUSH_VAR_WORD,
|
||||||
|
Opcode.PUSH_VAR_BYTE,
|
||||||
|
Opcode.PUSH_MEM_B, Opcode.PUSH_MEM_UB,
|
||||||
|
Opcode.PUSH_MEM_W, Opcode.PUSH_MEM_UW,
|
||||||
|
Opcode.PUSH_MEM_FLOAT -> optimizeDiscardAfterPush(it[0].index, it[1].index, it[1].value)
|
||||||
|
else -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (rins in instructionsToReplace) {
|
||||||
|
blk.instructions[rins.key] = rins.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun variable(scopedname: String, decl: VarDecl) {
|
||||||
|
val value = when(decl.datatype) {
|
||||||
|
DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT -> Value(decl.datatype, (decl.value as LiteralValue).asNumericValue!!)
|
||||||
|
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> {
|
||||||
|
val litval = (decl.value as LiteralValue)
|
||||||
|
if(litval.heapId==null)
|
||||||
|
throw CompilerException("string should already be in the heap")
|
||||||
|
Value(decl.datatype, litval.heapId)
|
||||||
|
}
|
||||||
|
DataType.ARRAY_B, DataType.ARRAY_W, DataType.MATRIX_B, DataType.MATRIX_UB,
|
||||||
|
DataType.ARRAY_UB, DataType.ARRAY_UW, DataType.ARRAY_F -> {
|
||||||
|
val litval = (decl.value as LiteralValue)
|
||||||
|
if(litval.heapId==null)
|
||||||
|
throw CompilerException("array/matrix should already be in the heap")
|
||||||
|
Value(decl.datatype, litval.heapId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentBlock.variables[scopedname] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun instr(opcode: Opcode, arg: Value? = null, callLabel: String? = null) {
|
||||||
|
currentBlock.instructions.add(Instruction(opcode, arg, callLabel))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun label(labelname: String) {
|
||||||
|
val instr = LabelInstr(labelname)
|
||||||
|
currentBlock.instructions.add(instr)
|
||||||
|
currentBlock.labels[labelname] = instr
|
||||||
|
}
|
||||||
|
|
||||||
|
fun line(position: Position) {
|
||||||
|
currentBlock.instructions.add(Instruction(Opcode.LINE, callLabel = "${position.line} ${position.file}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun newBlock(scopedname: String, address: Int?) {
|
||||||
|
currentBlock = ProgramBlock(scopedname, address)
|
||||||
|
blocks.add(currentBlock)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeCode(out: PrintStream, embeddedLabels: Boolean=true) {
|
||||||
|
out.println("; stackVM program code for '$name'")
|
||||||
|
out.println("%memory")
|
||||||
|
if(memory.isNotEmpty()) {
|
||||||
|
TODO("print out initial memory load")
|
||||||
|
}
|
||||||
|
out.println("%end_memory")
|
||||||
|
out.println("%heap")
|
||||||
|
heap.allStrings().forEach {
|
||||||
|
out.println("${it.index} ${it.value.type.toString().toLowerCase()} \"${it.value.str}\"")
|
||||||
|
}
|
||||||
|
heap.allArrays().forEach {
|
||||||
|
out.println("${it.index} ${it.value.type.toString().toLowerCase()} ${it.value.array!!.toList()}")
|
||||||
|
}
|
||||||
|
heap.allDoubleArrays().forEach {
|
||||||
|
out.println("${it.index} ${it.value.type.toString().toLowerCase()} ${it.value.doubleArray!!.toList()}")
|
||||||
|
}
|
||||||
|
out.println("%end_heap")
|
||||||
|
for(blk in blocks) {
|
||||||
|
out.println("\n%block ${blk.scopedname} ${blk.address?.toString(16) ?: ""}")
|
||||||
|
|
||||||
|
out.println("%variables")
|
||||||
|
for(variable in blk.variables) {
|
||||||
|
val valuestr = variable.value.toString()
|
||||||
|
out.println("${variable.key} ${variable.value.type.toString().toLowerCase()} $valuestr")
|
||||||
|
}
|
||||||
|
out.println("%end_variables")
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,25 +4,33 @@ import prog8.ast.DataType
|
|||||||
import prog8.compiler.HeapValues
|
import prog8.compiler.HeapValues
|
||||||
import prog8.compiler.unescape
|
import prog8.compiler.unescape
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.PrintStream
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
class Program (val name: String,
|
class Program (val name: String,
|
||||||
prog: MutableList<Instruction>,
|
val program: MutableList<Instruction>,
|
||||||
|
val variables: Map<String, Value>,
|
||||||
val labels: Map<String, Instruction>,
|
val labels: Map<String, Instruction>,
|
||||||
val variables: Map<String, Map<String, Value>>,
|
|
||||||
val memory: Map<Int, List<Value>>,
|
val memory: Map<Int, List<Value>>,
|
||||||
val heap: HeapValues)
|
val heap: HeapValues)
|
||||||
{
|
{
|
||||||
|
init {
|
||||||
|
// add end of program marker and some sentinel instructions, to correctly connect all others
|
||||||
|
program.add(LabelInstr("____program_end"))
|
||||||
|
program.add(Instruction(Opcode.TERMINATE))
|
||||||
|
program.add(Instruction(Opcode.NOP))
|
||||||
|
connect()
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun load(filename: String): Program {
|
fun load(filename: String): Program {
|
||||||
val lines = File(filename).readLines().withIndex().iterator()
|
val lines = File(filename).readLines().withIndex().iterator()
|
||||||
val memory = mutableMapOf<Int, List<Value>>()
|
val memory = mutableMapOf<Int, List<Value>>()
|
||||||
val vars = mutableMapOf<String, MutableMap<String, Value>>()
|
|
||||||
var instructions = mutableListOf<Instruction>()
|
|
||||||
var labels = mapOf<String, Instruction>()
|
|
||||||
val heap = HeapValues()
|
val heap = HeapValues()
|
||||||
|
val program = mutableListOf<Instruction>()
|
||||||
|
val variables = mutableMapOf<String, Value>()
|
||||||
|
val labels = mutableMapOf<String, Instruction>()
|
||||||
|
|
||||||
while(lines.hasNext()) {
|
while(lines.hasNext()) {
|
||||||
val (lineNr, line) = lines.next()
|
val (lineNr, line) = lines.next()
|
||||||
if(line.startsWith(';') || line.isEmpty())
|
if(line.startsWith(';') || line.isEmpty())
|
||||||
@ -31,16 +39,33 @@ class Program (val name: String,
|
|||||||
loadMemory(lines, memory)
|
loadMemory(lines, memory)
|
||||||
else if(line=="%heap")
|
else if(line=="%heap")
|
||||||
loadHeap(lines, heap)
|
loadHeap(lines, heap)
|
||||||
else if(line=="%variables")
|
else if(line.startsWith("%block "))
|
||||||
loadVars(lines, vars)
|
loadBlock(lines, heap, program, variables, labels)
|
||||||
else if(line=="%instructions") {
|
|
||||||
val (insResult, labelResult) = loadInstructions(lines, heap)
|
|
||||||
instructions = insResult
|
|
||||||
labels = labelResult
|
|
||||||
}
|
|
||||||
else throw VmExecutionException("syntax error at line ${lineNr + 1}")
|
else throw VmExecutionException("syntax error at line ${lineNr + 1}")
|
||||||
}
|
}
|
||||||
return Program(filename, instructions, labels, vars, memory, heap)
|
return Program(filename, program, variables, labels, memory, heap)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadBlock(lines: Iterator<IndexedValue<String>>,
|
||||||
|
heap: HeapValues,
|
||||||
|
program: MutableList<Instruction>,
|
||||||
|
variables: MutableMap<String, Value>,
|
||||||
|
labels: MutableMap<String, Instruction>)
|
||||||
|
{
|
||||||
|
while(true) {
|
||||||
|
val (lineNr, line) = lines.next()
|
||||||
|
if(line.isEmpty())
|
||||||
|
continue
|
||||||
|
else if(line=="%end_block")
|
||||||
|
return
|
||||||
|
else if(line=="%variables")
|
||||||
|
loadVars(lines, variables)
|
||||||
|
else if(line=="%instructions") {
|
||||||
|
val (blockInstructions, blockLabels) = loadInstructions(lines, heap)
|
||||||
|
program.addAll(blockInstructions)
|
||||||
|
labels.putAll(blockLabels)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadHeap(lines: Iterator<IndexedValue<String>>, heap: HeapValues) {
|
private fun loadHeap(lines: Iterator<IndexedValue<String>>, heap: HeapValues) {
|
||||||
@ -156,7 +181,7 @@ class Program (val name: String,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun loadVars(lines: Iterator<IndexedValue<String>>,
|
private fun loadVars(lines: Iterator<IndexedValue<String>>,
|
||||||
vars: MutableMap<String, MutableMap<String, Value>>): Map<String, Map<String, Value>> {
|
vars: MutableMap<String, Value>): Map<String, Value> {
|
||||||
val splitpattern = Pattern.compile("\\s+")
|
val splitpattern = Pattern.compile("\\s+")
|
||||||
while(true) {
|
while(true) {
|
||||||
val (lineNr, line) = lines.next()
|
val (lineNr, line) = lines.next()
|
||||||
@ -197,10 +222,7 @@ class Program (val name: String,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val blockname = name.substringBefore('.')
|
vars[name] = value
|
||||||
val blockvars = vars[blockname] ?: mutableMapOf()
|
|
||||||
vars[blockname] = blockvars
|
|
||||||
blockvars[name] = value
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,15 +251,6 @@ class Program (val name: String,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val program: List<Instruction>
|
|
||||||
|
|
||||||
init {
|
|
||||||
prog.add(LabelInstr("____program_end"))
|
|
||||||
prog.add(Instruction(Opcode.TERMINATE))
|
|
||||||
prog.add(Instruction(Opcode.NOP))
|
|
||||||
program = prog
|
|
||||||
connect()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun connect() {
|
private fun connect() {
|
||||||
val it1 = program.iterator()
|
val it1 = program.iterator()
|
||||||
@ -283,43 +296,4 @@ class Program (val name: String,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun print(out: PrintStream, embeddedLabels: Boolean=true) {
|
|
||||||
out.println("; stackVM program code for '$name'")
|
|
||||||
out.println("%memory")
|
|
||||||
if(memory.isNotEmpty()) {
|
|
||||||
TODO("print out initial memory load")
|
|
||||||
}
|
|
||||||
out.println("%end_memory")
|
|
||||||
out.println("%heap")
|
|
||||||
heap.allStrings().forEach {
|
|
||||||
out.println("${it.index} ${it.value.type.toString().toLowerCase()} \"${it.value.str}\"")
|
|
||||||
}
|
|
||||||
heap.allArrays().forEach {
|
|
||||||
out.println("${it.index} ${it.value.type.toString().toLowerCase()} ${it.value.array!!.toList()}")
|
|
||||||
}
|
|
||||||
heap.allDoubleArrays().forEach {
|
|
||||||
out.println("${it.index} ${it.value.type.toString().toLowerCase()} ${it.value.doubleArray!!.toList()}")
|
|
||||||
}
|
|
||||||
out.println("%end_heap")
|
|
||||||
out.println("%variables")
|
|
||||||
// just flatten all block vars into one global list for now...
|
|
||||||
for(variable in variables.flatMap { e->e.value.entries}) {
|
|
||||||
val valuestr = variable.value.toString()
|
|
||||||
out.println("${variable.key} ${variable.value.type.toString().toLowerCase()} $valuestr")
|
|
||||||
}
|
|
||||||
out.println("%end_variables")
|
|
||||||
out.println("%instructions")
|
|
||||||
val labels = this.labels.entries.associateBy({it.value}) {it.key}
|
|
||||||
for(instr in this.program) {
|
|
||||||
if(!embeddedLabels) {
|
|
||||||
val label = labels[instr]
|
|
||||||
if (label != null)
|
|
||||||
out.println("$label:")
|
|
||||||
} else {
|
|
||||||
out.println(instr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out.println("%end_instructions")
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -384,9 +384,7 @@ class StackVm(private var traceOutputFile: String?) {
|
|||||||
this.heap = program.heap
|
this.heap = program.heap
|
||||||
this.canvas = canvas
|
this.canvas = canvas
|
||||||
canvas?.requestFocusInWindow()
|
canvas?.requestFocusInWindow()
|
||||||
variables.clear()
|
variables = program.variables.toMutableMap()
|
||||||
for(variable in program.variables.flatMap { e->e.value.entries })
|
|
||||||
variables[variable.key] = variable.value
|
|
||||||
|
|
||||||
if("A" in variables || "X" in variables || "Y" in variables ||
|
if("A" in variables || "X" in variables || "Y" in variables ||
|
||||||
"XY" in variables || "AX" in variables ||"AY" in variables)
|
"XY" in variables || "AX" in variables ||"AY" in variables)
|
||||||
|
@ -50,18 +50,8 @@ class TestStackVmOpcodes {
|
|||||||
vars: Map<String, Value>?=null,
|
vars: Map<String, Value>?=null,
|
||||||
labels: Map<String, Instruction>?=null,
|
labels: Map<String, Instruction>?=null,
|
||||||
mem: Map<Int, List<Value>>?=null) : Program {
|
mem: Map<Int, List<Value>>?=null) : Program {
|
||||||
|
|
||||||
val blockvars = mutableMapOf<String, MutableMap<String, Value>>()
|
|
||||||
if(vars!=null) {
|
|
||||||
for (blockvar in vars) {
|
|
||||||
val blockname = blockvar.key.substringBefore('.')
|
|
||||||
val variables = blockvars[blockname] ?: mutableMapOf()
|
|
||||||
blockvars[blockname] = variables
|
|
||||||
variables[blockvar.key] = blockvar.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val heap = HeapValues()
|
val heap = HeapValues()
|
||||||
return Program("test", ins, labels ?: mapOf(), blockvars, mem ?: mapOf(), heap)
|
return Program("test", ins, vars ?: mapOf(), labels ?: mapOf(), mem ?: mapOf(), heap)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -252,7 +242,6 @@ class TestStackVmOpcodes {
|
|||||||
assertEquals(42.25, vm.mem.getFloat(0x4000))
|
assertEquals(42.25, vm.mem.getFloat(0x4000))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testPopVar() {
|
fun testPopVar() {
|
||||||
val ins = mutableListOf(
|
val ins = mutableListOf(
|
||||||
|
@ -25,15 +25,28 @@ class TestCompiler {
|
|||||||
assertEquals("10", 10.toHex())
|
assertEquals("10", 10.toHex())
|
||||||
assertEquals("10", 10.99.toHex())
|
assertEquals("10", 10.99.toHex())
|
||||||
assertEquals("15", 15.toHex())
|
assertEquals("15", 15.toHex())
|
||||||
assertEquals("$10", 16.toHex())
|
assertEquals("\$10", 16.toHex())
|
||||||
assertEquals("\$ff", 255.toHex())
|
assertEquals("\$ff", 255.toHex())
|
||||||
assertEquals("$0100", 256.toHex())
|
assertEquals("\$0100", 256.toHex())
|
||||||
assertEquals("$4e5c", 20060.toHex())
|
assertEquals("\$4e5c", 20060.toHex())
|
||||||
|
assertEquals("\$c382", 50050.toHex())
|
||||||
assertEquals("\$ffff", 65535.toHex())
|
assertEquals("\$ffff", 65535.toHex())
|
||||||
assertEquals("\$ffff", 65535L.toHex())
|
assertEquals("\$ffff", 65535L.toHex())
|
||||||
|
assertEquals("0", 0.toHex())
|
||||||
|
assertEquals("-1", (-1).toHex())
|
||||||
|
assertEquals("-1", (-1.234).toHex())
|
||||||
|
assertEquals("-10", (-10).toHex())
|
||||||
|
assertEquals("-10", (-10.99).toHex())
|
||||||
|
assertEquals("-15", (-15).toHex())
|
||||||
|
assertEquals("-\$10", (-16).toHex())
|
||||||
|
assertEquals("-\$ff", (-255).toHex())
|
||||||
|
assertEquals("-\$0100", (-256).toHex())
|
||||||
|
assertEquals("-\$4e5c", (-20060).toHex())
|
||||||
|
assertEquals("-\$c382", (-50050).toHex())
|
||||||
|
assertEquals("-\$ffff", (-65535).toHex())
|
||||||
|
assertEquals("-\$ffff", (-65535L).toHex())
|
||||||
assertFailsWith<CompilerException> { 65536.toHex() }
|
assertFailsWith<CompilerException> { 65536.toHex() }
|
||||||
assertFailsWith<CompilerException> { (-1).toHex() }
|
assertFailsWith<CompilerException> { 65536L.toHex() }
|
||||||
assertFailsWith<CompilerException> { (-1.99).toHex() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user