mirror of
https://github.com/irmen/prog8.git
synced 2024-09-28 17:54:58 +00:00
p8ir file format is now valid XML
This commit is contained in:
parent
136a9a39de
commit
9d7b9771c2
@ -53,5 +53,5 @@ class VirtualMachineDefinition: IMachineDefinition {
|
||||
}
|
||||
|
||||
interface IVirtualMachineRunner {
|
||||
fun runProgram(irSource: CharSequence)
|
||||
fun runProgram(irSource: String)
|
||||
}
|
||||
|
@ -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>
|
||||
""")
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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")
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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>
|
||||
|
@ -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()
|
||||
|
@ -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>
|
||||
"""
|
||||
|
Loading…
Reference in New Issue
Block a user