p8ir file format is now valid XML

This commit is contained in:
Irmen de Jong 2022-11-11 23:35:52 +01:00
parent 136a9a39de
commit 9d7b9771c2
10 changed files with 71 additions and 66 deletions

View File

@ -53,5 +53,5 @@ class VirtualMachineDefinition: IMachineDefinition {
}
interface IVirtualMachineRunner {
fun runProgram(irSource: CharSequence)
fun runProgram(irSource: String)
}

View File

@ -11,7 +11,8 @@ class TestLaunchEmu: FunSpec({
test("test launch virtualmachine via target") {
val target = VMTarget()
val tmpfile = kotlin.io.path.createTempFile(suffix=".p8ir")
tmpfile.writeText("""<PROGRAM NAME=test>
tmpfile.writeText("""<?xml version="1.0" encoding="utf-8"?>
<PROGRAM NAME="test">
<OPTIONS>
</OPTIONS>
@ -30,7 +31,7 @@ class TestLaunchEmu: FunSpec({
<INITGLOBALS>
</INITGLOBALS>
<BLOCK NAME=main ADDRESS=null ALIGN=NONE POS=[unittest: line 42 col 1-9]>
<BLOCK NAME="main" ADDRESS="null" ALIGN="NONE" POS="[unittest: line 42 col 1-9]">
</BLOCK>
</PROGRAM>
""")

View File

@ -3,6 +3,8 @@ TODO
For next release
^^^^^^^^^^^^^^^^
- fix IR/VM compiler crash: subroutine address in array: uword[] @shared ptrs = [&x1, &x2, &start] + unit test
- IRFileReader should parse the p8ir file with xml parser
- 6502 codegen: make it possible to use cpu opcodes such as 'nop' as variable names by prefixing all asm vars with something such as ``p8v_``? Or not worth it (most 3 letter opcodes as variables are nonsensical anyway)
then we can get rid of the instruction lists in the machinedefinitions as well. This is already no problem at all in the IR codegen.
- create BSS section in output program and put StStaticVariables in there with bss=true. Don't forget to add init code to zero out everything that was put in bss. If array in bss->only zero ONCE! So requires self-modifying code

View File

@ -1,35 +1,18 @@
%import textio
%import psg
%zeropage basicsafe
main {
sub start() {
ubyte @shared variable
uword[] @shared ptrs = [&x1, &x2, &start, 4242, 4242]
ubyte x1
ubyte x2
sub nested() {
ubyte @shared variable2
variable2 = 33
nested()
sub nested() {
ubyte @shared variable3
variable3 = 33
}
}
nested()
explosion()
}
sub explosion() {
; this subroutine is not used but it is an example of how to make a sound effect using the psg library!
psg.silent()
psg.voice(0, psg.LEFT, 63, psg.NOISE, 0)
psg.voice(1, psg.RIGHT, 63, psg.NOISE, 0)
psg.freq(0, 1000)
psg.freq(1, 2000)
txt.print_uwhex(ptrs[0], true)
txt.spc()
txt.print_uwhex(ptrs[1], true)
txt.spc()
txt.print_uwhex(ptrs[2], true)
txt.nl()
}
}

View File

@ -10,19 +10,34 @@ import kotlin.io.path.bufferedReader
class IRFileReader {
fun read(irSourceCode: CharSequence): IRProgram {
fun read(irSourceCode: String): IRProgram {
// TODO parse the source via XML parser
// val isrc = InputSource(StringReader(irSourceCode))
// val xmlDoc: Document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(isrc)
// xmlDoc.normalizeDocument()
// println("ROOT NODE: ${xmlDoc.documentElement.nodeName}") // TODO
return parseProgram(irSourceCode.lineSequence().iterator())
}
fun read(irSourceFile: Path): IRProgram {
println("Reading intermediate representation from $irSourceFile")
irSourceFile.bufferedReader().use { reader ->
// TODO parse the source via XML parser
// val xmlDoc: Document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(irSourceFile.toFile())
// xmlDoc.normalizeDocument()
// println("ROOT NODE: ${xmlDoc.documentElement.nodeName}") // TODO
irSourceFile.bufferedReader(charset=Charsets.UTF_8).use { reader ->
return parseProgram(reader.lineSequence().iterator())
}
}
private fun parseProgram(lines: Iterator<String>): IRProgram {
val programPattern = Regex("<PROGRAM NAME=(.+)>")
val xmlheader = lines.next()
if(!xmlheader.startsWith("<?xml version=\"1.0\""))
throw IRParseException("missing xml header")
val programPattern = Regex("<PROGRAM NAME=\"(.+)\">")
val line = lines.next()
val match = programPattern.matchEntire(line) ?: throw IRParseException("invalid PROGRAM")
val programName = match.groups[1]!!.value
@ -171,7 +186,7 @@ class IRFileReader {
} else {
bss = false
initArray = value.split(',').map {
if (it.startsWith('&'))
if (it.startsWith('@'))
StArrayElement(null, it.drop(1).split('.'))
else
StArrayElement(parseIRValue(it).toDouble(), null)
@ -194,7 +209,7 @@ class IRFileReader {
if(line!="<MEMORYMAPPEDVARIABLES>")
throw IRParseException("invalid MEMORYMAPPEDVARIABLES")
val memvars = mutableListOf<StMemVar>()
val mappedPattern = Regex("&(.+?)(\\[.+?\\])? (.+)=(.+)")
val mappedPattern = Regex("@(.+?)(\\[.+?\\])? (.+)=(.+)")
while(true) {
line = lines.next()
if(line=="</MEMORYMAPPEDVARIABLES>")
@ -286,11 +301,11 @@ class IRFileReader {
return blocks
}
private val blockPattern = Regex("<BLOCK NAME=(.+) ADDRESS=(.*) ALIGN=(.+) POS=(.+)>")
private val inlineAsmPattern = Regex("<INLINEASM LABEL=(.*) IR=(.+)>")
private val bytesPattern = Regex("<BYTES LABEL=(.*)>")
private val asmsubPattern = Regex("<ASMSUB NAME=(.+) ADDRESS=(.+) CLOBBERS=(.*) RETURNS=(.*) POS=(.+)>")
private val subPattern = Regex("<SUB NAME=(.+) RETURNTYPE=(.+) POS=(.+)>")
private val blockPattern = Regex("<BLOCK NAME=\"(.+)\" ADDRESS=\"(.*)\" ALIGN=\"(.+)\" POS=\"(.+)\">")
private val inlineAsmPattern = Regex("<INLINEASM LABEL=\"(.*)\" IR=\"(.+)\">")
private val bytesPattern = Regex("<BYTES LABEL=\"(.*)\">")
private val asmsubPattern = Regex("<ASMSUB NAME=\"(.+)\" ADDRESS=\"(.+)\" CLOBBERS=\"(.*)\" RETURNS=\"(.*)\" POS=\"(.+)\">")
private val subPattern = Regex("<SUB NAME=\"(.+)\" RETURNTYPE=\"(.+)\" POS=\"(.+)\">")
private val posPattern = Regex("\\[(.+): line (.+) col (.+)-(.+)\\]")
private fun parseBlock(startline: String, lines: Iterator<String>): IRBlock {
@ -445,8 +460,8 @@ class IRFileReader {
else
throw IRParseException("invalid or empty <C>ODE chunk")
}
val label = if(firstline.startsWith("<C LABEL="))
firstline.split('=', limit = 2)[1].dropLast(1)
val label = if(firstline.startsWith("<C LABEL=\""))
firstline.split('=', limit = 2)[1].dropLast(1).trim('"')
else
null
val chunk = IRCodeChunk(label, null)

View File

@ -8,14 +8,15 @@ import kotlin.io.path.div
class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
private val outfile = outfileOverride ?: (irProgram.options.outputDir / ("${irProgram.name}.p8ir"))
private val out = outfile.bufferedWriter()
private val out = outfile.bufferedWriter(charset=Charsets.UTF_8)
private var numChunks = 0
private var numInstr = 0
fun write(): Path {
println("Writing intermediate representation to $outfile")
out.write("<PROGRAM NAME=${irProgram.name}>\n")
out.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")
out.write("<PROGRAM NAME=\"${irProgram.name}\">\n")
writeOptions()
writeAsmSymbols()
writeVariableAllocations()
@ -44,12 +45,12 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
private fun writeBlocks() {
irProgram.blocks.forEach { block ->
out.write("\n<BLOCK NAME=${block.name} ADDRESS=${block.address?.toHex()} ALIGN=${block.alignment} POS=${block.position}>\n")
out.write("\n<BLOCK NAME=\"${block.name}\" ADDRESS=\"${block.address?.toHex()}\" ALIGN=\"${block.alignment}\" POS=\"${block.position}\">\n")
block.inlineAssembly.forEach {
writeInlineAsm(it)
}
block.subroutines.forEach {
out.write("<SUB NAME=${it.name} RETURNTYPE=${it.returnType.toString().lowercase()} POS=${it.position}>\n")
out.write("<SUB NAME=\"${it.name}\" RETURNTYPE=\"${it.returnType.toString().lowercase()}\" POS=\"${it.position}\">\n")
out.write("<PARAMS>\n")
it.parameters.forEach { param -> out.write("${getTypeString(param.dt)} ${param.name}\n") }
out.write("</PARAMS>\n")
@ -70,7 +71,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
if(reg.registerOrPair!=null) "${reg.registerOrPair}:${dt.toString().lowercase()}"
else "${reg.statusflag}:${dt.toString().lowercase()}"
}.joinToString(",")
out.write("<ASMSUB NAME=${it.name} ADDRESS=${it.address?.toHex()} CLOBBERS=$clobbers RETURNS=$returns POS=${it.position}>\n")
out.write("<ASMSUB NAME=\"${it.name}\" ADDRESS=\"${it.address?.toHex()}\" CLOBBERS=\"$clobbers\" RETURNS=\"$returns\" POS=\"${it.position}\">\n")
out.write("<PARAMS>\n")
it.parameters.forEach { (dt, regOrSf) ->
val reg = if(regOrSf.registerOrPair!=null) regOrSf.registerOrPair.toString()
@ -87,7 +88,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
private fun writeCodeChunk(chunk: IRCodeChunk) {
if(chunk.label!=null)
out.write("<C LABEL=${chunk.label}>\n")
out.write("<C LABEL=\"${chunk.label}\">\n")
else
out.write("<C>\n")
chunk.instructions.forEach { instr ->
@ -99,7 +100,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
}
private fun writeInlineBytes(chunk: IRInlineBinaryChunk) {
out.write("<BYTES LABEL=${chunk.label ?: ""}>\n")
out.write("<BYTES LABEL=\"${chunk.label ?: ""}\">\n")
chunk.data.withIndex().forEach {(index, byte) ->
out.write(byte.toString(16).padStart(2,'0'))
if(index and 63 == 63 && index < chunk.data.size-1)
@ -109,7 +110,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
}
private fun writeInlineAsm(chunk: IRInlineAsmChunk) {
out.write("<INLINEASM LABEL=${chunk.label ?: ""} IR=${chunk.isIR}>\n")
out.write("<INLINEASM LABEL=\"${chunk.label ?: ""}\" IR=\"${chunk.isIR}\">\n")
out.write(chunk.assembly)
out.write("\n</INLINEASM>\n")
}
@ -157,7 +158,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
if(it.number!=null)
it.number!!.toInt().toHex()
else
"&${it.addressOf!!.joinToString(".")}"
"@${it.addressOf!!.joinToString(".")}"
}
} else {
"" // array will be zero'd out at program start
@ -172,7 +173,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
out.write("\n<MEMORYMAPPEDVARIABLES>\n")
for (variable in irProgram.st.allMemMappedVariables()) {
val typeStr = getTypeString(variable)
out.write("&$typeStr ${variable.name}=${variable.address.toHex()}\n")
out.write("@$typeStr ${variable.name}=${variable.address.toHex()}\n")
}
out.write("</MEMORYMAPPEDVARIABLES>\n")

View File

@ -521,8 +521,8 @@ data class InstructionFormat(val datatype: IRDataType?,
/*
<X = X is not modified (input/readonly value)
>X = X is overwritten with output value (output value)
<>X = X is modified (input + output)
TODO: also encode if memory is read/written/modified?
<>X = X is modified (read as input + written as output)
TODO: also encode if *memory* is read/written/modified?
*/
@Suppress("BooleanLiteralArgument")
val instructionFormats = mutableMapOf(

View File

@ -33,14 +33,16 @@ class TestIRFileInOut: FunSpec({
val writer = IRFileWriter(program, null)
val generatedFile = writer.write()
val lines = generatedFile.readLines()
lines.first() shouldBe "<PROGRAM NAME=unittest-irwriter>"
lines[0] shouldBe "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
lines[1] shouldBe "<PROGRAM NAME=\"unittest-irwriter\">"
lines.last() shouldBe "</PROGRAM>"
generatedFile.deleteExisting()
lines.size shouldBeGreaterThan 20
}
test("test IR reader") {
val source="""<PROGRAM NAME=test-ir-reader>
val source="""<?xml version="1.0" encoding="utf-8"?>
<PROGRAM NAME="test-ir-reader">
<OPTIONS>
compTarget=virtual
output=PRG
@ -59,7 +61,7 @@ uword sys.wait.jiffies= zp=DONTCARE
</VARIABLES>
<MEMORYMAPPEDVARIABLES>
&uword cx16.r0=65282
@uword cx16.r0=65282
</MEMORYMAPPEDVARIABLES>
<MEMORYSLABS>
@ -71,22 +73,22 @@ load.b r1,42
</C>
</INITGLOBALS>
<BLOCK NAME=main ADDRESS=null ALIGN=NONE POS=[examples/test.p8: line 2 col 2-5]>
<SUB NAME=main.start RETURNTYPE=null POS=[examples/test.p8: line 4 col 6-8]>
<BLOCK NAME="main" ADDRESS="null" ALIGN="NONE" POS="[examples/test.p8: line 2 col 2-5]">
<SUB NAME="main.start" RETURNTYPE="null" POS="[examples/test.p8: line 4 col 6-8]">
<PARAMS>
</PARAMS>
<C LABEL=main.start>
<C LABEL="main.start">
return
</C>
</SUB>
</BLOCK>
<BLOCK NAME=sys ADDRESS=null ALIGN=NONE POS=[library:/prog8lib/virtual/syslib.p8: line 3 col 2-4]>
<SUB NAME=sys.wait RETURNTYPE=null POS=[library:/prog8lib/virtual/syslib.p8: line 15 col 6-8]>
<BLOCK NAME="sys" ADDRESS="null" ALIGN="NONE" POS="[library:/prog8lib/virtual/syslib.p8: line 3 col 2-4]">
<SUB NAME="sys.wait" RETURNTYPE="null" POS="[library:/prog8lib/virtual/syslib.p8: line 15 col 6-8]">
<PARAMS>
uword sys.wait.jiffies
</PARAMS>
<INLINEASM LABEL=sys.wait IR=true POS=[library:/prog8lib/virtual/syslib.p8: line 17 col 10-13]>
<INLINEASM LABEL="sys.wait" IR="true" POS="[library:/prog8lib/virtual/syslib.p8: line 17 col 10-13]">
loadm.w r0,sys.wait.jiffies
syscall 13
</INLINEASM>

View File

@ -2155,11 +2155,11 @@ class VirtualMachine(irProgram: IRProgram) {
// probably called via reflection
class VmRunner: IVirtualMachineRunner {
override fun runProgram(irSource: CharSequence) {
override fun runProgram(irSource: String) {
runAndTestProgram(irSource) { /* no tests */ }
}
fun runAndTestProgram(irSource: CharSequence, test: (VirtualMachine) -> Unit) {
fun runAndTestProgram(irSource: String, test: (VirtualMachine) -> Unit) {
val irProgram = IRFileReader().read(irSource)
val vm = VirtualMachine(irProgram)
vm.run()

View File

@ -120,7 +120,8 @@ class TestVm: FunSpec( {
test("vmrunner") {
val runner = VmRunner()
val irSource="""<PROGRAM NAME=test>
val irSource="""<?xml version="1.0" encoding="utf-8"?>
<PROGRAM NAME="test">
<OPTIONS>
</OPTIONS>
@ -139,7 +140,7 @@ class TestVm: FunSpec( {
<INITGLOBALS>
</INITGLOBALS>
<BLOCK NAME=main ADDRESS=null ALIGN=NONE POS=[unittest: line 42 col 1-9]>
<BLOCK NAME="main" ADDRESS="null" ALIGN="NONE" POS="[unittest: line 42 col 1-9]">
</BLOCK>
</PROGRAM>
"""