mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
using builder to improve massive lists of constructor params
This commit is contained in:
1 parent
a60ba27bad
commit
930697dad5
16 files changed
+686
-212
No files matched your search
@@ -43,5 +43,81 @@ class CompilationOptions(val output: OutputType,
|
||||
|
||||
companion object {
|
||||
val AllZeropageAllowed: List<UIntRange> = listOf(0u..255u)
|
||||
|
||||
fun builder(compTarget: ICompilationTarget) = Builder(compTarget)
|
||||
|
||||
class Builder(private val compTarget: ICompilationTarget) {
|
||||
private var output: OutputType = compTarget.defaultOutputType
|
||||
private var launcher: CbmPrgLauncherType = CbmPrgLauncherType.NONE
|
||||
private var zeropage: ZeropageType = ZeropageType.DONTUSE
|
||||
private var zpReserved: List<UIntRange> = emptyList()
|
||||
private var zpAllowed: List<UIntRange> = AllZeropageAllowed
|
||||
private var floats: Boolean = false
|
||||
private var noSysInit: Boolean = false
|
||||
private var romable: Boolean = false
|
||||
private var compilerVersion: String = "unknown"
|
||||
private var loadAddress: UInt = compTarget.PROGRAM_LOAD_ADDRESS
|
||||
private var memtopAddress: UInt = compTarget.PROGRAM_MEMTOP_ADDRESS
|
||||
private var warnSymbolShadowing: Boolean = false
|
||||
private var warnImplicitTypeCast: Boolean = false
|
||||
private var optimize: Boolean = false
|
||||
private var asmQuiet: Boolean = false
|
||||
private var asmListfile: Boolean = false
|
||||
private var includeSourcelines: Boolean = false
|
||||
private var dumpVariables: Boolean = false
|
||||
private var dumpSymbols: Boolean = false
|
||||
private var experimentalCodegen: Boolean = false
|
||||
private var varsHighBank: Int? = null
|
||||
private var varsGolden: Boolean = false
|
||||
private var slabsHighBank: Int? = null
|
||||
private var slabsGolden: Boolean = false
|
||||
private var breakpointCpuInstruction: String? = null
|
||||
private var ignoreFootguns: Boolean = false
|
||||
private var outputDir: Path = Path("")
|
||||
private var quiet: Boolean = false
|
||||
private var profilingInstrumentation: Boolean = false
|
||||
private var symbolDefs: Map<String, String> = emptyMap()
|
||||
|
||||
fun output(output: OutputType) = apply { this.output = output }
|
||||
fun launcher(launcher: CbmPrgLauncherType) = apply { this.launcher = launcher }
|
||||
fun zeropage(zeropage: ZeropageType) = apply { this.zeropage = zeropage }
|
||||
fun zpReserved(zpReserved: List<UIntRange>) = apply { this.zpReserved = zpReserved }
|
||||
fun zpAllowed(zpAllowed: List<UIntRange>) = apply { this.zpAllowed = zpAllowed }
|
||||
fun floats(floats: Boolean) = apply { this.floats = floats }
|
||||
fun noSysInit(noSysInit: Boolean) = apply { this.noSysInit = noSysInit }
|
||||
fun romable(romable: Boolean) = apply { this.romable = romable }
|
||||
fun compilerVersion(compilerVersion: String) = apply { this.compilerVersion = compilerVersion }
|
||||
fun loadAddress(loadAddress: UInt) = apply { this.loadAddress = loadAddress }
|
||||
fun memtopAddress(memtopAddress: UInt) = apply { this.memtopAddress = memtopAddress }
|
||||
fun warnSymbolShadowing(warnSymbolShadowing: Boolean) = apply { this.warnSymbolShadowing = warnSymbolShadowing }
|
||||
fun warnImplicitTypeCast(warnImplicitTypeCast: Boolean) = apply { this.warnImplicitTypeCast = warnImplicitTypeCast }
|
||||
fun optimize(optimize: Boolean) = apply { this.optimize = optimize }
|
||||
fun asmQuiet(asmQuiet: Boolean) = apply { this.asmQuiet = asmQuiet }
|
||||
fun asmListfile(asmListfile: Boolean) = apply { this.asmListfile = asmListfile }
|
||||
fun includeSourcelines(includeSourcelines: Boolean) = apply { this.includeSourcelines = includeSourcelines }
|
||||
fun dumpVariables(dumpVariables: Boolean) = apply { this.dumpVariables = dumpVariables }
|
||||
fun dumpSymbols(dumpSymbols: Boolean) = apply { this.dumpSymbols = dumpSymbols }
|
||||
fun experimentalCodegen(experimentalCodegen: Boolean) = apply { this.experimentalCodegen = experimentalCodegen }
|
||||
fun varsHighBank(varsHighBank: Int?) = apply { this.varsHighBank = varsHighBank }
|
||||
fun varsGolden(varsGolden: Boolean) = apply { this.varsGolden = varsGolden }
|
||||
fun slabsHighBank(slabsHighBank: Int?) = apply { this.slabsHighBank = slabsHighBank }
|
||||
fun slabsGolden(slabsGolden: Boolean) = apply { this.slabsGolden = slabsGolden }
|
||||
fun breakpointCpuInstruction(breakpointCpuInstruction: String?) = apply { this.breakpointCpuInstruction = breakpointCpuInstruction }
|
||||
fun ignoreFootguns(ignoreFootguns: Boolean) = apply { this.ignoreFootguns = ignoreFootguns }
|
||||
fun outputDir(outputDir: Path) = apply { this.outputDir = outputDir }
|
||||
fun quiet(quiet: Boolean) = apply { this.quiet = quiet }
|
||||
fun profilingInstrumentation(profilingInstrumentation: Boolean) = apply { this.profilingInstrumentation = profilingInstrumentation }
|
||||
fun symbolDefs(symbolDefs: Map<String, String>) = apply { this.symbolDefs = symbolDefs }
|
||||
|
||||
fun build(): CompilationOptions {
|
||||
return CompilationOptions(
|
||||
output, launcher, zeropage, zpReserved, zpAllowed, floats, noSysInit, romable, compTarget, compilerVersion,
|
||||
loadAddress, memtopAddress, warnSymbolShadowing, warnImplicitTypeCast, optimize, asmQuiet, asmListfile,
|
||||
includeSourcelines, dumpVariables, dumpSymbols, experimentalCodegen, varsHighBank, varsGolden,
|
||||
slabsHighBank, slabsGolden, breakpointCpuInstruction, ignoreFootguns, outputDir, quiet,
|
||||
profilingInstrumentation, symbolDefs
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,20 +33,12 @@ class TestZeropageFreeList: FunSpec({
|
||||
// ============================================================================
|
||||
|
||||
fun createOptions(target: ICompilationTarget, zpType: ZeropageType, floats: Boolean = false): CompilationOptions {
|
||||
return CompilationOptions(
|
||||
output = OutputType.RAW,
|
||||
launcher = CbmPrgLauncherType.NONE,
|
||||
zeropage = zpType,
|
||||
zpReserved = emptyList(),
|
||||
zpAllowed = CompilationOptions.AllZeropageAllowed,
|
||||
floats = floats,
|
||||
noSysInit = false,
|
||||
romable = false,
|
||||
compTarget = target,
|
||||
compilerVersion = "test",
|
||||
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
||||
memtopAddress = target.PROGRAM_MEMTOP_ADDRESS
|
||||
)
|
||||
return CompilationOptions.builder(target)
|
||||
.output(OutputType.RAW)
|
||||
.zeropage(zpType)
|
||||
.floats(floats)
|
||||
.compilerVersion("test")
|
||||
.build()
|
||||
}
|
||||
|
||||
fun verifyScratchLocationsReserved(zp: Zeropage) {
|
||||
|
||||
@@ -24,20 +24,13 @@ class TestCodegen: FunSpec({
|
||||
|
||||
fun getTestOptions(): CompilationOptions {
|
||||
val target = C64Target()
|
||||
return CompilationOptions(
|
||||
OutputType.RAW,
|
||||
CbmPrgLauncherType.NONE,
|
||||
ZeropageType.DONTUSE,
|
||||
zpReserved = emptyList(),
|
||||
zpAllowed = CompilationOptions.AllZeropageAllowed,
|
||||
floats = true,
|
||||
noSysInit = false,
|
||||
romable = false,
|
||||
compTarget = target,
|
||||
compilerVersion="99.99",
|
||||
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
||||
memtopAddress = 0xffffu
|
||||
)
|
||||
return CompilationOptions.builder(target)
|
||||
.output(OutputType.RAW)
|
||||
.zeropage(ZeropageType.DONTUSE)
|
||||
.floats(true)
|
||||
.compilerVersion("99.99")
|
||||
.memtopAddress(0xffffu)
|
||||
.build()
|
||||
}
|
||||
|
||||
test("augmented assign on arrays") {
|
||||
|
||||
@@ -14,20 +14,12 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
chunks.forEach { sub += it }
|
||||
block += sub
|
||||
val target = VMTarget()
|
||||
val options = CompilationOptions(
|
||||
OutputType.RAW,
|
||||
CbmPrgLauncherType.NONE,
|
||||
ZeropageType.DONTUSE,
|
||||
emptyList(),
|
||||
CompilationOptions.AllZeropageAllowed,
|
||||
floats = false,
|
||||
noSysInit = true,
|
||||
romable = false,
|
||||
compTarget = target,
|
||||
compilerVersion="99.99",
|
||||
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
||||
memtopAddress = 0xffffu
|
||||
)
|
||||
val options = CompilationOptions.builder(target)
|
||||
.output(OutputType.RAW)
|
||||
.zeropage(ZeropageType.DONTUSE)
|
||||
.noSysInit(true)
|
||||
.compilerVersion("99.99")
|
||||
.build()
|
||||
val prog = IRProgram("test", IRSymbolTable(), options, target)
|
||||
prog.addBlock(block)
|
||||
prog.linkChunks()
|
||||
|
||||
@@ -19,20 +19,12 @@ class TestVmCodeGen: FunSpec({
|
||||
|
||||
fun getTestOptions(): CompilationOptions {
|
||||
val target = VMTarget()
|
||||
return CompilationOptions(
|
||||
OutputType.RAW,
|
||||
CbmPrgLauncherType.NONE,
|
||||
ZeropageType.DONTUSE,
|
||||
zpReserved = emptyList(),
|
||||
zpAllowed = CompilationOptions.AllZeropageAllowed,
|
||||
floats = true,
|
||||
noSysInit = false,
|
||||
romable = false,
|
||||
compTarget = target,
|
||||
compilerVersion="99.99",
|
||||
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
||||
memtopAddress = 0xffffu
|
||||
)
|
||||
return CompilationOptions.builder(target)
|
||||
.output(OutputType.RAW)
|
||||
.zeropage(ZeropageType.DONTUSE)
|
||||
.floats(true)
|
||||
.compilerVersion("99.99")
|
||||
.build()
|
||||
}
|
||||
|
||||
test("augmented assigns") {
|
||||
|
||||
@@ -461,7 +461,7 @@ internal fun determineCompilationOptions(program: Program, compTarget: ICompilat
|
||||
val allOptions = program.modules.flatMap { it.options() }.toSet()
|
||||
val floatsEnabled = "enable_floats" in allOptions
|
||||
var noSysInit = "no_sysinit" in allOptions
|
||||
val rombale = "romable" in allOptions
|
||||
val romable = "romable" in allOptions
|
||||
var zpType: ZeropageType =
|
||||
if (zpoption == null)
|
||||
if (floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE
|
||||
@@ -516,11 +516,17 @@ internal fun determineCompilationOptions(program: Program, compTarget: ICompilat
|
||||
noSysInit = true
|
||||
}
|
||||
|
||||
return CompilationOptions(
|
||||
outputType, launcherType,
|
||||
zpType, zpReserved, zpAllowed, floatsEnabled, noSysInit, rombale,
|
||||
compTarget, VERSION, 0u, 0xffffu
|
||||
)
|
||||
return CompilationOptions.builder(compTarget)
|
||||
.output(outputType)
|
||||
.launcher(launcherType)
|
||||
.zeropage(zpType)
|
||||
.zpReserved(zpReserved)
|
||||
.zpAllowed(if (zpAllowed.isEmpty()) CompilationOptions.AllZeropageAllowed else zpAllowed)
|
||||
.floats(floatsEnabled)
|
||||
.noSysInit(noSysInit)
|
||||
.romable(romable)
|
||||
.compilerVersion(VERSION)
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun processAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
package prog8tests.compiler
|
||||
|
||||
import com.github.michaelbull.result.expectError
|
||||
import com.github.michaelbull.result.getOrElse
|
||||
import com.github.michaelbull.result.onErr
|
||||
@@ -20,62 +19,45 @@ import prog8.code.target.zp.C64Zeropage
|
||||
import prog8.code.target.zp.CX16Zeropage
|
||||
import prog8.code.target.zp.PETZeropage
|
||||
import prog8tests.helpers.ErrorReporterForTests
|
||||
|
||||
|
||||
class TestAbstractZeropage: FunSpec({
|
||||
|
||||
class DummyZeropage(options: CompilationOptions) : Zeropage(options) {
|
||||
override val SCRATCH_B1 = 0x10u
|
||||
override val SCRATCH_REG = 0x11u
|
||||
override val SCRATCH_W1 = 0x20u
|
||||
override val SCRATCH_W2 = 0x30u
|
||||
override val SCRATCH_PTR = 0x40u
|
||||
|
||||
init {
|
||||
free.addAll(0u..255u)
|
||||
|
||||
removeReservedFromFreePool()
|
||||
retainAllowed()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
test("testAbstractZeropage") {
|
||||
val zp = DummyZeropage(
|
||||
CompilationOptions(
|
||||
OutputType.RAW,
|
||||
CbmPrgLauncherType.NONE,
|
||||
ZeropageType.FULL,
|
||||
listOf((0x50u..0x5fu)),
|
||||
CompilationOptions.AllZeropageAllowed,
|
||||
floats = false,
|
||||
noSysInit = false,
|
||||
romable = false,
|
||||
compTarget = C64Target(),
|
||||
compilerVersion="99.99",
|
||||
loadAddress = 999u,
|
||||
memtopAddress = 0xffffu
|
||||
)
|
||||
CompilationOptions.builder(C64Target())
|
||||
.output(OutputType.RAW)
|
||||
.zeropage(ZeropageType.FULL)
|
||||
.zpReserved(listOf(0x50u..0x5fu))
|
||||
.compilerVersion("99.99")
|
||||
.loadAddress(999u)
|
||||
.memtopAddress(0xffffu)
|
||||
.build()
|
||||
)
|
||||
zp.free.size shouldBe 256-8-16
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
|
||||
class TestC64Zeropage: FunSpec({
|
||||
|
||||
val errors = ErrorReporterForTests()
|
||||
val c64target = C64Target()
|
||||
|
||||
test("testNames") {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed,
|
||||
floats = false,
|
||||
noSysInit = false,
|
||||
romable = false,
|
||||
compTarget = c64target, compilerVersion="99.99", loadAddress = 999u, memtopAddress = 0xffffu
|
||||
))
|
||||
|
||||
val zp = C64Zeropage(CompilationOptions.builder(c64target)
|
||||
.output(OutputType.RAW)
|
||||
.zeropage(ZeropageType.BASICSAFE)
|
||||
.compilerVersion("99.99")
|
||||
.loadAddress(999u)
|
||||
.memtopAddress(0xffffu)
|
||||
.build()
|
||||
)
|
||||
var result = zp.allocate("", DataType.UBYTE, null, null, errors)
|
||||
result.onErr { error(it.toString()) }
|
||||
result = zp.allocate("", DataType.UBYTE, null, null, errors)
|
||||
@@ -86,50 +68,59 @@ class TestC64Zeropage: FunSpec({
|
||||
result = zp.allocate("varname2", DataType.UBYTE, null, null, errors)
|
||||
result.onErr { error(it.toString()) }
|
||||
}
|
||||
|
||||
test("testZpFloatEnable") {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
val zp = C64Zeropage(CompilationOptions.builder(c64target).output(OutputType.RAW).zeropage(ZeropageType.FULL).compilerVersion("99.99").loadAddress(999u).memtopAddress(0xffffu).build())
|
||||
var result = zp.allocate("", DataType.FLOAT, null, null, errors)
|
||||
result.expectError { "should be allocation error due to disabled floats" }
|
||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.DONTUSE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
val zp2 = C64Zeropage(CompilationOptions.builder(c64target).output(OutputType.RAW).zeropage(ZeropageType.DONTUSE).floats(true).compilerVersion("99.99").loadAddress(999u).memtopAddress(0xffffu).build())
|
||||
result = zp2.allocate("", DataType.FLOAT, null, null, errors)
|
||||
result.expectError { "should be allocation error due to disabled ZP use" }
|
||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
val zp3 = C64Zeropage(CompilationOptions.builder(c64target).output(OutputType.RAW).zeropage(ZeropageType.FLOATSAFE).floats(true).compilerVersion("99.99").loadAddress(999u).memtopAddress(0xffffu).build())
|
||||
zp3.allocate("", DataType.FLOAT, null, null, errors)
|
||||
}
|
||||
|
||||
test("testZpModesWithFloats") {
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
C64Zeropage(CompilationOptions.builder(c64target).output(OutputType.RAW).zeropage(ZeropageType.FULL).compilerVersion("99.99").loadAddress(999u).memtopAddress(0xffffu).build())
|
||||
C64Zeropage(CompilationOptions.builder(c64target).output(OutputType.RAW).zeropage(ZeropageType.KERNALSAFE).compilerVersion("99.99").loadAddress(999u).memtopAddress(0xffffu).build())
|
||||
C64Zeropage(CompilationOptions.builder(c64target).output(OutputType.RAW).zeropage(ZeropageType.BASICSAFE).compilerVersion("99.99").loadAddress(999u).memtopAddress(0xffffu).build())
|
||||
C64Zeropage(CompilationOptions.builder(c64target).output(OutputType.RAW).zeropage(ZeropageType.FLOATSAFE).compilerVersion("99.99").loadAddress(999u).memtopAddress(0xffffu).build())
|
||||
C64Zeropage(CompilationOptions.builder(c64target).output(OutputType.RAW).zeropage(ZeropageType.BASICSAFE).floats(true).compilerVersion("99.99").loadAddress(999u).memtopAddress(0xffffu).build())
|
||||
C64Zeropage(CompilationOptions.builder(c64target).output(OutputType.RAW).zeropage(ZeropageType.FLOATSAFE).floats(true).compilerVersion("99.99").loadAddress(999u).memtopAddress(0xffffu).build())
|
||||
shouldThrow<InternalCompilerException> {
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
C64Zeropage(CompilationOptions.builder(c64target).output(OutputType.RAW).zeropage(ZeropageType.FULL).floats(true).compilerVersion("99.99").loadAddress(999u).memtopAddress(0xffffu).build())
|
||||
}
|
||||
shouldThrow<InternalCompilerException> {
|
||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
C64Zeropage(CompilationOptions.builder(c64target).output(OutputType.RAW).zeropage(ZeropageType.KERNALSAFE).floats(true).compilerVersion("99.99").loadAddress(999u).memtopAddress(0xffffu).build())
|
||||
}
|
||||
}
|
||||
|
||||
test("testZpDontuse") {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.DONTUSE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
val zp = C64Zeropage(CompilationOptions.builder(c64target)
|
||||
.output(OutputType.RAW)
|
||||
.zeropage(ZeropageType.DONTUSE)
|
||||
.compilerVersion("99.99")
|
||||
.loadAddress(999u)
|
||||
.memtopAddress(0xffffu)
|
||||
.build()
|
||||
)
|
||||
println(zp.free)
|
||||
zp.availableBytes() shouldBe 0
|
||||
val result = zp.allocate("", DataType.BYTE, null, null, errors)
|
||||
result.expectError { "expected error due to disabled ZP use" }
|
||||
}
|
||||
|
||||
test("testBasicsafeAllocation") {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
val zp = C64Zeropage(CompilationOptions.builder(c64target)
|
||||
.output(OutputType.RAW)
|
||||
.zeropage(ZeropageType.BASICSAFE)
|
||||
.floats(true)
|
||||
.compilerVersion("99.99")
|
||||
.loadAddress(999u)
|
||||
.memtopAddress(0xffffu)
|
||||
.build()
|
||||
)
|
||||
zp.availableBytes() shouldBe 14
|
||||
zp.hasByteAvailable() shouldBe true
|
||||
zp.hasWordAvailable() shouldBe true
|
||||
|
||||
var result = zp.allocate("", DataType.FLOAT, null, null, errors)
|
||||
result.expectError { "expect allocation error: in regular zp there aren't 5 sequential bytes free" }
|
||||
|
||||
(0 until zp.availableBytes()).forEach {
|
||||
val alloc = zp.allocate("", DataType.UBYTE, null, null, errors)
|
||||
alloc.getOrElse { throw it }
|
||||
@@ -142,9 +133,15 @@ class TestC64Zeropage: FunSpec({
|
||||
result = zp.allocate("", DataType.UWORD, null, null, errors)
|
||||
result.expectError { "expected allocation error" }
|
||||
}
|
||||
|
||||
test("testFullAllocation") {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
val zp = C64Zeropage(CompilationOptions.builder(c64target)
|
||||
.output(OutputType.RAW)
|
||||
.zeropage(ZeropageType.FULL)
|
||||
.compilerVersion("99.99")
|
||||
.loadAddress(999u)
|
||||
.memtopAddress(0xffffu)
|
||||
.build()
|
||||
)
|
||||
zp.availableBytes() shouldBe 207
|
||||
zp.hasByteAvailable() shouldBe true
|
||||
zp.hasWordAvailable() shouldBe true
|
||||
@@ -154,29 +151,32 @@ class TestC64Zeropage: FunSpec({
|
||||
loc shouldBeGreaterThan 3u
|
||||
loc shouldNotBeIn zp.free
|
||||
val num = zp.availableBytes() / 2
|
||||
|
||||
(0..num).forEach {
|
||||
zp.allocate("", DataType.UWORD, null, null, errors)
|
||||
}
|
||||
zp.availableBytes() shouldBe 5
|
||||
|
||||
// can't allocate because no more sequential bytes, only fragmented
|
||||
result = zp.allocate("", DataType.UWORD, null, null, errors)
|
||||
result.expectError { "should give allocation error" }
|
||||
|
||||
(0..10).forEach {
|
||||
zp.allocate("", DataType.UBYTE, null, null, errors)
|
||||
}
|
||||
|
||||
zp.availableBytes() shouldBe 0
|
||||
zp.hasByteAvailable() shouldBe false
|
||||
zp.hasWordAvailable() shouldBe false
|
||||
result = zp.allocate("", DataType.UBYTE, null, null, errors)
|
||||
result.expectError { "should give allocation error" }
|
||||
}
|
||||
|
||||
test("testEfficientAllocation") {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
val zp = C64Zeropage(CompilationOptions.builder(c64target)
|
||||
.output(OutputType.RAW)
|
||||
.zeropage(ZeropageType.BASICSAFE)
|
||||
.floats(true)
|
||||
.compilerVersion("99.99")
|
||||
.loadAddress(999u)
|
||||
.memtopAddress(0xffffu)
|
||||
.build()
|
||||
)
|
||||
zp.availableBytes() shouldBe 14
|
||||
zp.allocate("", DataType.WORD, null, null, errors).getOrElse{throw it}.address shouldBe 0x9eu
|
||||
zp.allocate("", DataType.UBYTE, null, null, errors).getOrElse{throw it}.address shouldBe 0x06u
|
||||
@@ -191,48 +191,75 @@ class TestC64Zeropage: FunSpec({
|
||||
zp.allocate("", DataType.UBYTE, null, null, errors).getOrElse{throw it}.address shouldBe 0xf9u
|
||||
zp.availableBytes() shouldBe 0
|
||||
}
|
||||
|
||||
test("testLongAllocation") {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
val zp = C64Zeropage(CompilationOptions.builder(c64target)
|
||||
.output(OutputType.RAW)
|
||||
.zeropage(ZeropageType.FULL)
|
||||
.compilerVersion("99.99")
|
||||
.loadAddress(999u)
|
||||
.memtopAddress(0xffffu)
|
||||
.build()
|
||||
)
|
||||
zp.availableBytes() shouldBe 207
|
||||
|
||||
// LONG requires 4 sequential bytes
|
||||
val result = zp.allocate("", DataType.LONG, null, null, errors)
|
||||
result.onErr { error(it.toString()) }
|
||||
zp.availableBytes() shouldBe 203 // 207 - 4
|
||||
|
||||
// Allocate several LONGs
|
||||
(1..10).forEach {
|
||||
zp.allocate("", DataType.LONG, null, null, errors).onErr { error(it.toString()) }
|
||||
}
|
||||
zp.availableBytes() shouldBe 163 // 203 - 40
|
||||
}
|
||||
|
||||
test("testFloatAllocation") {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
|
||||
val zp = C64Zeropage(CompilationOptions.builder(c64target)
|
||||
.output(OutputType.RAW)
|
||||
.zeropage(ZeropageType.FLOATSAFE)
|
||||
.floats(true)
|
||||
.compilerVersion("99.99")
|
||||
.loadAddress(999u)
|
||||
.memtopAddress(0xffffu)
|
||||
.build()
|
||||
)
|
||||
// FLOAT requires 5 sequential bytes and floats must be enabled
|
||||
val initialFree = zp.availableBytes()
|
||||
val result = zp.allocate("", DataType.FLOAT, null, null, errors)
|
||||
result.onErr { error(it.toString()) }
|
||||
zp.availableBytes() shouldBe initialFree - 5 // FLOAT is 5 bytes
|
||||
|
||||
// Verify warning is generated for large allocation
|
||||
errors.warnings.any { it.contains("float") && it.contains("5 bytes") } shouldBe true
|
||||
}
|
||||
|
||||
test("testReservedLocations") {
|
||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
val zp = C64Zeropage(CompilationOptions.builder(c64target)
|
||||
.output(OutputType.RAW)
|
||||
.zeropage(ZeropageType.BASICSAFE)
|
||||
.compilerVersion("99.99")
|
||||
.loadAddress(999u)
|
||||
.memtopAddress(0xffffu)
|
||||
.build()
|
||||
)
|
||||
withClue("zp _B1 and _REG must be next to each other to create a word") {
|
||||
zp.SCRATCH_B1 + 1u shouldBe zp.SCRATCH_REG
|
||||
}
|
||||
}
|
||||
|
||||
test("virtual registers") {
|
||||
val zpbasic = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
val zpbasic = C64Zeropage(CompilationOptions.builder(c64target)
|
||||
.output(OutputType.RAW)
|
||||
.zeropage(ZeropageType.BASICSAFE)
|
||||
.compilerVersion("99.99")
|
||||
.loadAddress(999u)
|
||||
.memtopAddress(0xffffu)
|
||||
.build()
|
||||
)
|
||||
zpbasic.allocatedVariables.any {it.key=="cx16.r0"} shouldBe false
|
||||
|
||||
val zpkernal = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||
val zpkernal = C64Zeropage(CompilationOptions.builder(c64target)
|
||||
.output(OutputType.RAW)
|
||||
.zeropage(ZeropageType.KERNALSAFE)
|
||||
.compilerVersion("99.99")
|
||||
.loadAddress(999u)
|
||||
.memtopAddress(0xffffu)
|
||||
.build()
|
||||
)
|
||||
zpkernal.allocatedVariables.any {it.key=="cx16.r0"} shouldBe true
|
||||
val r0bL = zpkernal.allocatedVariables.getValue("cx16.r0bL")
|
||||
r0bL.size shouldBe 1
|
||||
@@ -244,13 +271,25 @@ class TestC64Zeropage: FunSpec({
|
||||
r14r15sl.address shouldBe 32u
|
||||
}
|
||||
})
|
||||
|
||||
class TestPetZeropage: FunSpec({
|
||||
test("virtual registers") {
|
||||
val zpbasic = PETZeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, PETTarget(), "99.99", 999u, 0xffffu))
|
||||
val zpbasic = PETZeropage(CompilationOptions.builder(PETTarget())
|
||||
.output(OutputType.RAW)
|
||||
.zeropage(ZeropageType.BASICSAFE)
|
||||
.compilerVersion("99.99")
|
||||
.loadAddress(999u)
|
||||
.memtopAddress(0xffffu)
|
||||
.build()
|
||||
)
|
||||
zpbasic.allocatedVariables.any {it.key=="cx16.r0"} shouldBe false
|
||||
|
||||
val zpkernal = PETZeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, PETTarget(), "99.99", 999u, 0xffffu))
|
||||
val zpkernal = PETZeropage(CompilationOptions.builder(PETTarget())
|
||||
.output(OutputType.RAW)
|
||||
.zeropage(ZeropageType.KERNALSAFE)
|
||||
.compilerVersion("99.99")
|
||||
.loadAddress(999u)
|
||||
.memtopAddress(0xffffu)
|
||||
.build()
|
||||
)
|
||||
zpkernal.allocatedVariables.any {it.key=="cx16.r0"} shouldBe true
|
||||
val r0bL = zpkernal.allocatedVariables.getValue("cx16.r0bL")
|
||||
r0bL.size shouldBe 1
|
||||
@@ -262,13 +301,25 @@ class TestPetZeropage: FunSpec({
|
||||
r14r15sl.address shouldBe 32u
|
||||
}
|
||||
})
|
||||
|
||||
class TestC128Zeropage: FunSpec({
|
||||
test("virtual registers") {
|
||||
val zpbasic = C128Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, C128Target(), "99.99", 999u, 0xffffu))
|
||||
val zpbasic = C128Zeropage(CompilationOptions.builder(C128Target())
|
||||
.output(OutputType.RAW)
|
||||
.zeropage(ZeropageType.BASICSAFE)
|
||||
.compilerVersion("99.99")
|
||||
.loadAddress(999u)
|
||||
.memtopAddress(0xffffu)
|
||||
.build()
|
||||
)
|
||||
zpbasic.allocatedVariables.any {it.key=="cx16.r0"} shouldBe false
|
||||
|
||||
val zpkernal = C128Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, C128Target(), "99.99", 999u, 0xffffu))
|
||||
val zpkernal = C128Zeropage(CompilationOptions.builder(C128Target())
|
||||
.output(OutputType.RAW)
|
||||
.zeropage(ZeropageType.KERNALSAFE)
|
||||
.compilerVersion("99.99")
|
||||
.loadAddress(999u)
|
||||
.memtopAddress(0xffffu)
|
||||
.build()
|
||||
)
|
||||
zpkernal.allocatedVariables.any {it.key=="cx16.r0"} shouldBe true
|
||||
val r0bL = zpkernal.allocatedVariables.getValue("cx16.r0bL")
|
||||
r0bL.size shouldBe 1
|
||||
@@ -280,20 +331,31 @@ class TestC128Zeropage: FunSpec({
|
||||
r14r15sl.address shouldBe 38u
|
||||
}
|
||||
})
|
||||
|
||||
class TestCx16Zeropage: FunSpec({
|
||||
val errors = ErrorReporterForTests()
|
||||
val cx16target = Cx16Target()
|
||||
|
||||
test("testReservedLocations") {
|
||||
val zp = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, cx16target, "99.99", 999u, 0xffffu))
|
||||
val zp = CX16Zeropage(CompilationOptions.builder(cx16target)
|
||||
.output(OutputType.RAW)
|
||||
.zeropage(ZeropageType.BASICSAFE)
|
||||
.compilerVersion("99.99")
|
||||
.loadAddress(999u)
|
||||
.memtopAddress(0xffffu)
|
||||
.build()
|
||||
)
|
||||
withClue("zp _B1 and _REG must be next to each other to create a word") {
|
||||
zp.SCRATCH_B1 + 1u shouldBe zp.SCRATCH_REG
|
||||
}
|
||||
}
|
||||
|
||||
test("preallocated zp vars") {
|
||||
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, cx16target, "99.99", 999u, 0xffffu))
|
||||
val zp1 = CX16Zeropage(CompilationOptions.builder(cx16target)
|
||||
.output(OutputType.RAW)
|
||||
.zeropage(ZeropageType.FULL)
|
||||
.compilerVersion("99.99")
|
||||
.loadAddress(999u)
|
||||
.memtopAddress(0xffffu)
|
||||
.build()
|
||||
)
|
||||
zp1.allocatedVariables["test"] shouldBe null
|
||||
zp1.allocatedVariables["cx16.r0"] shouldNotBe null
|
||||
zp1.allocatedVariables["cx16.r15"] shouldNotBe null
|
||||
@@ -302,12 +364,24 @@ class TestCx16Zeropage: FunSpec({
|
||||
zp1.allocatedVariables["cx16.r0sH"] shouldNotBe null
|
||||
zp1.allocatedVariables["cx16.r15sH"] shouldNotBe null
|
||||
}
|
||||
|
||||
test("virtual registers") {
|
||||
val zpbasic = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, Cx16Target(), "99.99", 999u, 0xffffu))
|
||||
val zpbasic = CX16Zeropage(CompilationOptions.builder(Cx16Target())
|
||||
.output(OutputType.RAW)
|
||||
.zeropage(ZeropageType.BASICSAFE)
|
||||
.compilerVersion("99.99")
|
||||
.loadAddress(999u)
|
||||
.memtopAddress(0xffffu)
|
||||
.build()
|
||||
)
|
||||
zpbasic.allocatedVariables.any {it.key=="cx16.r0"} shouldBe true
|
||||
|
||||
val zpkernal = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, Cx16Target(), "99.99", 999u, 0xffffu))
|
||||
val zpkernal = CX16Zeropage(CompilationOptions.builder(Cx16Target())
|
||||
.output(OutputType.RAW)
|
||||
.zeropage(ZeropageType.KERNALSAFE)
|
||||
.compilerVersion("99.99")
|
||||
.loadAddress(999u)
|
||||
.memtopAddress(0xffffu)
|
||||
.build()
|
||||
)
|
||||
zpkernal.allocatedVariables.any {it.key=="cx16.r0"} shouldBe true
|
||||
val r0bL = zpkernal.allocatedVariables.getValue("cx16.r0bL")
|
||||
r0bL.size shouldBe 1
|
||||
@@ -318,32 +392,26 @@ class TestCx16Zeropage: FunSpec({
|
||||
r14r15sl.dt shouldBe DataType.LONG
|
||||
r14r15sl.address shouldBe 30u
|
||||
}
|
||||
|
||||
test("testLongAllocation") {
|
||||
val zp = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, cx16target, "99.99", 999u, 0xffffu))
|
||||
zp.availableBytes() shouldBe 214
|
||||
|
||||
// LONG requires 4 sequential bytes
|
||||
val result = zp.allocate("", DataType.LONG, null, null, errors)
|
||||
result.onErr { error(it.toString()) }
|
||||
zp.availableBytes() shouldBe 210 // 214 - 4
|
||||
|
||||
// Allocate several LONGs
|
||||
(1..10).forEach {
|
||||
zp.allocate("", DataType.LONG, null, null, errors).onErr { error(it.toString()) }
|
||||
}
|
||||
zp.availableBytes() shouldBe 170 // 210 - 40
|
||||
}
|
||||
|
||||
test("testFloatAllocation") {
|
||||
val zp = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, cx16target, "99.99", 999u, 0xffffu))
|
||||
|
||||
// FLOAT requires 5 sequential bytes and floats must be enabled
|
||||
val initialFree = zp.availableBytes()
|
||||
val result = zp.allocate("", DataType.FLOAT, null, null, errors)
|
||||
result.onErr { error(it.toString()) }
|
||||
zp.availableBytes() shouldBe initialFree - 5 // FLOAT is 5 bytes
|
||||
|
||||
// Verify warning is generated for large allocation
|
||||
errors.warnings.any { it.contains("float") && it.contains("5 bytes") } shouldBe true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
package prog8tests.ast
|
||||
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.shouldBe
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.code.core.*
|
||||
|
||||
class TestBuilders : FunSpec({
|
||||
val pos = Position("test", 1, 1, 1)
|
||||
|
||||
context("VarDecl builder") {
|
||||
test("VarDecl builder basic usage") {
|
||||
val decl = VarDecl.builder(DataType.UBYTE, pos)
|
||||
.names("myvar")
|
||||
.build()
|
||||
|
||||
decl.name shouldBe "myvar"
|
||||
decl.datatype shouldBe DataType.UBYTE
|
||||
decl.type shouldBe VarDeclType.VAR
|
||||
decl.origin shouldBe VarDeclOrigin.USERCODE
|
||||
decl.zeropage shouldBe ZeropageWish.DONTCARE
|
||||
decl.splitwordarray shouldBe SplitWish.DONTCARE
|
||||
decl.isPrivate shouldBe false
|
||||
decl.names.isEmpty() shouldBe true
|
||||
}
|
||||
|
||||
test("VarDecl builder multiple names") {
|
||||
val decl = VarDecl.builder(DataType.UBYTE, pos)
|
||||
.names("v1", "v2", "v3")
|
||||
.build()
|
||||
|
||||
decl.name shouldBe "<multiple>"
|
||||
decl.names shouldBe listOf("v1", "v2", "v3")
|
||||
}
|
||||
|
||||
test("VarDecl builder advanced properties") {
|
||||
val valExpr = NumericLiteral.optimalInteger(42, pos)
|
||||
val numColsExpr = NumericLiteral.optimalInteger(10, pos)
|
||||
val arraySize = ArrayIndex(NumericLiteral.optimalInteger(100, pos), pos)
|
||||
|
||||
val decl = VarDecl.builder(DataType.UWORD, pos)
|
||||
.names("myconst")
|
||||
.type(VarDeclType.CONST)
|
||||
.origin(VarDeclOrigin.ARRAYLITERAL)
|
||||
.zeropage(ZeropageWish.REQUIRE_ZEROPAGE)
|
||||
.splitwordarray(SplitWish.NOSPLIT)
|
||||
.isPrivate(true)
|
||||
.sharedWithAsm(true)
|
||||
.alignment(4u)
|
||||
.dirty(true)
|
||||
.value(valExpr)
|
||||
.matrixNumCols(numColsExpr)
|
||||
.arraysize(arraySize)
|
||||
.build()
|
||||
|
||||
decl.name shouldBe "myconst"
|
||||
decl.datatype shouldBe DataType.UWORD
|
||||
decl.type shouldBe VarDeclType.CONST
|
||||
decl.origin shouldBe VarDeclOrigin.ARRAYLITERAL
|
||||
decl.zeropage shouldBe ZeropageWish.REQUIRE_ZEROPAGE
|
||||
decl.splitwordarray shouldBe SplitWish.NOSPLIT
|
||||
decl.isPrivate shouldBe true
|
||||
decl.sharedWithAsm shouldBe true
|
||||
decl.alignment shouldBe 4u
|
||||
decl.dirty shouldBe true
|
||||
decl.value shouldBe valExpr
|
||||
decl.matrixNumCols shouldBe numColsExpr
|
||||
decl.arraysize shouldBe arraySize
|
||||
}
|
||||
|
||||
test("VarDecl builder name is required") {
|
||||
val builder = VarDecl.builder(DataType.UBYTE, pos)
|
||||
val exception = io.kotest.assertions.throwables.shouldThrow<IllegalStateException> {
|
||||
builder.build()
|
||||
}
|
||||
exception.message shouldBe "name is required"
|
||||
}
|
||||
|
||||
test("VarDecl builder at least one name required") {
|
||||
val builder = VarDecl.builder(DataType.UBYTE, pos)
|
||||
io.kotest.assertions.throwables.shouldThrow<IllegalArgumentException> {
|
||||
builder.names()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context("CompilationOptions builder") {
|
||||
test("CompilationOptions builder basic usage") {
|
||||
val target = prog8.code.target.C64Target()
|
||||
val options = CompilationOptions.builder(target).build()
|
||||
|
||||
options.compTarget shouldBe target
|
||||
options.output shouldBe target.defaultOutputType
|
||||
options.launcher shouldBe CbmPrgLauncherType.NONE
|
||||
options.zeropage shouldBe ZeropageType.DONTUSE
|
||||
options.compilerVersion shouldBe "unknown"
|
||||
options.loadAddress shouldBe target.PROGRAM_LOAD_ADDRESS
|
||||
options.memtopAddress shouldBe target.PROGRAM_MEMTOP_ADDRESS
|
||||
}
|
||||
|
||||
test("CompilationOptions builder custom properties") {
|
||||
val target = prog8.code.target.Cx16Target()
|
||||
val options = CompilationOptions.builder(target)
|
||||
.output(OutputType.RAW)
|
||||
.launcher(CbmPrgLauncherType.BASIC)
|
||||
.zeropage(ZeropageType.FLOATSAFE)
|
||||
.floats(true)
|
||||
.optimize(true)
|
||||
.warnSymbolShadowing(true)
|
||||
.build()
|
||||
|
||||
options.compTarget shouldBe target
|
||||
options.output shouldBe OutputType.RAW
|
||||
options.launcher shouldBe CbmPrgLauncherType.BASIC
|
||||
options.zeropage shouldBe ZeropageType.FLOATSAFE
|
||||
options.floats shouldBe true
|
||||
options.optimize shouldBe true
|
||||
options.warnSymbolShadowing shouldBe true
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -88,7 +88,14 @@ class TestAsmGenSymbols: StringSpec({
|
||||
|
||||
fun createTestAsmGen6502(program: Program): AsmGen6502Internal {
|
||||
val errors = ErrorReporterForTests()
|
||||
val options = CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, true, false, C64Target(), "99.99", 999u, 0xffffu)
|
||||
val options = CompilationOptions.builder(C64Target())
|
||||
.output(OutputType.RAW)
|
||||
.zeropage(ZeropageType.FULL)
|
||||
.noSysInit(true)
|
||||
.compilerVersion("99.99")
|
||||
.loadAddress(999u)
|
||||
.memtopAddress(0xffffu)
|
||||
.build()
|
||||
val astchecker = AstChecker(program, errors, options)
|
||||
astchecker.visit(program)
|
||||
errors.report()
|
||||
|
||||
@@ -273,6 +273,52 @@ class VarDecl(
|
||||
companion object {
|
||||
private var autoHeapValueSequenceNumber = 0
|
||||
|
||||
fun builder(datatype: DataType, position: Position) = Builder(datatype, position)
|
||||
|
||||
class Builder(
|
||||
private val datatype: DataType,
|
||||
private val position: Position
|
||||
) {
|
||||
private var name: String? = null
|
||||
private var type = VarDeclType.VAR
|
||||
private var origin = VarDeclOrigin.USERCODE
|
||||
private var zeropage = ZeropageWish.DONTCARE
|
||||
private var splitwordarray = SplitWish.DONTCARE
|
||||
private var arraysize: ArrayIndex? = null
|
||||
private var matrixNumCols: Expression? = null
|
||||
private var additionalNames: List<String> = emptyList()
|
||||
private var value: Expression? = null
|
||||
private var sharedWithAsm = false
|
||||
private var alignment = 0u
|
||||
private var dirty = false
|
||||
private var isPrivate = false
|
||||
|
||||
fun names(vararg names: String): Builder = apply {
|
||||
require(names.isNotEmpty()) { "at least one name is required" }
|
||||
this.name = if(names.size > 1) "<multiple>" else names[0]
|
||||
this.additionalNames = if(names.size > 1) names.toList() else emptyList()
|
||||
}
|
||||
fun type(t: VarDeclType) = apply { this.type = t }
|
||||
fun origin(o: VarDeclOrigin) = apply { this.origin = o }
|
||||
fun zeropage(z: ZeropageWish) = apply { this.zeropage = z }
|
||||
fun splitwordarray(s: SplitWish) = apply { this.splitwordarray = s }
|
||||
fun arraysize(a: ArrayIndex?) = apply { this.arraysize = a }
|
||||
fun matrixNumCols(m: Expression?) = apply { this.matrixNumCols = m }
|
||||
fun value(v: Expression?) = apply { this.value = v }
|
||||
fun sharedWithAsm(s: Boolean) = apply { this.sharedWithAsm = s }
|
||||
fun alignment(a: UInt) = apply { this.alignment = a }
|
||||
fun dirty(d: Boolean) = apply { this.dirty = d }
|
||||
fun isPrivate(p: Boolean) = apply { this.isPrivate = p }
|
||||
|
||||
fun build(): VarDecl {
|
||||
val finalName = name ?: throw IllegalStateException("name is required")
|
||||
return VarDecl(
|
||||
type, origin, datatype, zeropage, splitwordarray, arraysize, matrixNumCols,
|
||||
finalName, additionalNames, value, sharedWithAsm, alignment, dirty, isPrivate, position
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun fromParameter(param: SubroutineParameter): VarDecl {
|
||||
val decltype: VarDeclType
|
||||
val value: Expression?
|
||||
@@ -416,7 +462,6 @@ class VarDecl(
|
||||
|
||||
class StructDecl(override val name: String, val fields: Array<Pair<DataType, String>>, override val position: Position) : Statement(), INamedStatement, ISubType {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
}
|
||||
@@ -466,7 +511,6 @@ class StructFieldRef(val pointer: IdentifierReference, val struct: StructDecl, v
|
||||
|
||||
class Enumeration(override val name: String, val type: BaseDataType, val members: Array<Pair<String, Int?>>, override val position: Position) : Statement(), INamedStatement {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
}
|
||||
@@ -1485,7 +1529,6 @@ class OnGoto(
|
||||
) : Statement() {
|
||||
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
index.linkParents(this)
|
||||
|
||||
@@ -22,7 +22,7 @@ Weird Heisenbug
|
||||
|
||||
Future Things and Ideas
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
- kotlin: use builder patterns instead of humongous constructor parameter lists (vardecl mostly)?
|
||||
- kotlin: use builder patterns instead of humongous constructor parameter lists (vardecl mostly)? (PARTIALLY DONE: VarDecl now has a builder in its companion object)
|
||||
- symboldump: some sort of javadocs generated from the p8 source files (instead of just the function signatures). Use markdown for formatting.
|
||||
- why are (interned) strings stored as initialization value in the SymbolTable AND as string nodes in the interned string block? Something seems redundant here?
|
||||
- when implementing unsigned longs: remove the (multiple?) "TODO "hack" to allow unsigned long constants to be used as values for signed longs, without needing a cast
|
||||
|
||||
@@ -150,22 +150,19 @@ class IRFileReader {
|
||||
}
|
||||
}
|
||||
|
||||
return CompilationOptions(
|
||||
outputType,
|
||||
launcher,
|
||||
zeropage,
|
||||
zpReserved,
|
||||
zpAllowed,
|
||||
false,
|
||||
false,
|
||||
romable,
|
||||
target,
|
||||
compilerVersion,
|
||||
loadAddress,
|
||||
memtop,
|
||||
outputDir = outputDir,
|
||||
optimize = optimize
|
||||
)
|
||||
return CompilationOptions.builder(target)
|
||||
.output(outputType)
|
||||
.launcher(launcher)
|
||||
.zeropage(zeropage)
|
||||
.zpReserved(zpReserved)
|
||||
.zpAllowed(zpAllowed)
|
||||
.romable(romable)
|
||||
.compilerVersion(compilerVersion)
|
||||
.loadAddress(loadAddress)
|
||||
.memtopAddress(memtop)
|
||||
.outputDir(outputDir)
|
||||
.optimize(optimize)
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun parseAsmSymbols(reader: XMLEventReader): Map<String, String> {
|
||||
|
||||
@@ -13,21 +13,15 @@ class TestIRFileInOut: FunSpec({
|
||||
test("test IR writer") {
|
||||
val target = Cx16Target()
|
||||
val tempdir = Path(System.getProperty("java.io.tmpdir"))
|
||||
val options = CompilationOptions(
|
||||
OutputType.RAW,
|
||||
CbmPrgLauncherType.NONE,
|
||||
ZeropageType.DONTUSE,
|
||||
emptyList(),
|
||||
CompilationOptions.AllZeropageAllowed,
|
||||
floats = false,
|
||||
noSysInit = true,
|
||||
romable = false,
|
||||
compTarget = target,
|
||||
compilerVersion = "99.99",
|
||||
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
||||
memtopAddress = 0xffffu,
|
||||
outputDir = tempdir
|
||||
)
|
||||
val options = CompilationOptions.builder(target)
|
||||
.output(OutputType.RAW)
|
||||
.zeropage(ZeropageType.DONTUSE)
|
||||
.noSysInit(true)
|
||||
.compilerVersion("99.99")
|
||||
.loadAddress(target.PROGRAM_LOAD_ADDRESS)
|
||||
.memtopAddress(0xffffu)
|
||||
.outputDir(tempdir)
|
||||
.build()
|
||||
val program = IRProgram("unittest-irwriter", IRSymbolTable(), options, target)
|
||||
val writer = IRFileWriter(program, null)
|
||||
val generatedFile = writer.write()
|
||||
|
||||
@@ -31,6 +31,30 @@ class PtAsmSub(
|
||||
) : PtNamedNode(name, position), IPtSubroutine {
|
||||
|
||||
class Address(val constbank: UByte?, var varbank: PtIdentifier?, val address: UInt)
|
||||
|
||||
companion object {
|
||||
fun builder(name: String, position: Position) = Builder(name, position)
|
||||
}
|
||||
|
||||
class Builder(val name: String, val position: Position) {
|
||||
private var address: Address? = null
|
||||
private var clobbers: Set<CpuRegister> = emptySet()
|
||||
private var parameters: MutableList<Pair<RegisterOrStatusflag, PtSubroutineParameter>> = mutableListOf()
|
||||
private var returns: MutableList<Pair<RegisterOrStatusflag, DataType>> = mutableListOf()
|
||||
private var inline: Boolean = false
|
||||
|
||||
fun address(a: Address?) = apply { address = a }
|
||||
fun clobbers(c: Set<CpuRegister>) = apply { clobbers = c }
|
||||
fun parameters(p: Iterable<Pair<RegisterOrStatusflag, PtSubroutineParameter>>) = apply { parameters = p.toMutableList() }
|
||||
fun parameters(vararg p: Pair<RegisterOrStatusflag, PtSubroutineParameter>) = apply { parameters = p.toMutableList() }
|
||||
fun addParameter(reg: RegisterOrStatusflag, param: PtSubroutineParameter) = apply { parameters.add(reg to param) }
|
||||
fun returns(r: Iterable<Pair<RegisterOrStatusflag, DataType>>) = apply { returns = r.toMutableList() }
|
||||
fun returns(vararg r: Pair<RegisterOrStatusflag, DataType>) = apply { returns = r.toMutableList() }
|
||||
fun addReturn(reg: RegisterOrStatusflag, type: DataType) = apply { returns.add(reg to type) }
|
||||
fun inline(i: Boolean) = apply { inline = i }
|
||||
|
||||
fun build() = PtAsmSub(name, address, clobbers, parameters, returns, inline, position)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +70,24 @@ class PtSub(name: String, position: Position) : PtNamedNode(name, position), IPt
|
||||
get() = children[0] as PtSubSignature
|
||||
|
||||
override fun copy(): PtNode = PtSub(name, position)
|
||||
|
||||
companion object {
|
||||
fun builder(name: String, position: Position) = Builder(name, position)
|
||||
}
|
||||
|
||||
class Builder(val name: String, val position: Position) {
|
||||
private var parameters: MutableList<PtSubroutineParameter> = mutableListOf()
|
||||
private var returntypes: MutableList<DataType> = mutableListOf()
|
||||
|
||||
fun parameters(p: Iterable<PtSubroutineParameter>) = apply { parameters = p.toMutableList() }
|
||||
fun parameters(vararg p: PtSubroutineParameter) = apply { parameters = p.toMutableList() }
|
||||
fun addParameter(p: PtSubroutineParameter) = apply { parameters.add(p) }
|
||||
fun returntypes(r: Iterable<DataType>) = apply { returntypes = r.toMutableList() }
|
||||
fun returntypes(vararg r: DataType) = apply { returntypes = r.toMutableList() }
|
||||
fun addReturntype(t: DataType) = apply { returntypes.add(t) }
|
||||
|
||||
fun build() = PtSub(name, parameters, returntypes, position)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -321,6 +363,26 @@ class PtVariable(
|
||||
|
||||
value?.let {it.parent=this}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun builder(name: String, type: DataType, position: Position) = Builder(name, type, position)
|
||||
}
|
||||
|
||||
class Builder(val name: String, val type: DataType, val position: Position) {
|
||||
private var zeropage: ZeropageWish = ZeropageWish.DONTCARE
|
||||
private var align: UInt = 0u
|
||||
private var dirty: Boolean = false
|
||||
private var value: PtExpression? = null
|
||||
private var arraySize: UInt? = null
|
||||
|
||||
fun zeropage(z: ZeropageWish) = apply { zeropage = z }
|
||||
fun align(a: UInt) = apply { align = a }
|
||||
fun dirty(d: Boolean) = apply { dirty = d }
|
||||
fun value(v: PtExpression?) = apply { value = v }
|
||||
fun arraySize(s: UInt?) = apply { arraySize = s }
|
||||
|
||||
fun build() = PtVariable(name, type, zeropage, align, dirty, value, arraySize, position)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -330,18 +392,60 @@ class PtConstant(
|
||||
val value: Double?, // either a constant number...
|
||||
val memorySlab: StMemorySlab?, // .. or a memory() allocation
|
||||
position: Position
|
||||
) : PtNamedNode(name, position), IPtVariable
|
||||
) : PtNamedNode(name, position), IPtVariable {
|
||||
// note: a constant is a value but IS NOT a PtExpression node; all constants must have been replaced by their actual value
|
||||
|
||||
companion object {
|
||||
fun builder(name: String, type: DataType, position: Position) = Builder(name, type, position)
|
||||
}
|
||||
|
||||
class Builder(val name: String, val type: DataType, val position: Position) {
|
||||
private var value: Double? = null
|
||||
private var memorySlab: StMemorySlab? = null
|
||||
|
||||
fun value(v: Double?) = apply { value = v }
|
||||
fun memorySlab(s: StMemorySlab?) = apply { memorySlab = s }
|
||||
|
||||
fun build() = PtConstant(name, type, value, memorySlab, position)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtMemMapped(name: String, override val type: DataType, val address: UInt, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable {
|
||||
init {
|
||||
require(!type.isString)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun builder(name: String, type: DataType, address: UInt, position: Position) = Builder(name, type, address, position)
|
||||
}
|
||||
|
||||
class Builder(val name: String, val type: DataType, val address: UInt, val position: Position) {
|
||||
private var arraySize: UInt? = null
|
||||
|
||||
fun arraySize(s: UInt?) = apply { arraySize = s }
|
||||
|
||||
fun build() = PtMemMapped(name, type, address, arraySize, position)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtStructDecl(name: String, val fields: List<Pair<DataType, String>>, position: Position) : PtNamedNode(name, position)
|
||||
class PtStructDecl(name: String, val fields: List<Pair<DataType, String>>, position: Position) : PtNamedNode(name, position) {
|
||||
|
||||
companion object {
|
||||
fun builder(name: String, position: Position) = Builder(name, position)
|
||||
}
|
||||
|
||||
class Builder(val name: String, val position: Position) {
|
||||
private var fields: MutableList<Pair<DataType, String>> = mutableListOf()
|
||||
|
||||
fun fields(f: Iterable<Pair<DataType, String>>) = apply { fields = f.toMutableList() }
|
||||
fun fields(vararg f: Pair<DataType, String>) = apply { fields = f.toMutableList() }
|
||||
fun addField(type: DataType, name: String) = apply { fields.add(type to name) }
|
||||
|
||||
fun build() = PtStructDecl(name, fields, position)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PtWhen(position: Position) : PtNode(position) {
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package prog8tests.code
|
||||
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.collections.shouldHaveSize
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
|
||||
class TestPtBuilders : FunSpec({
|
||||
|
||||
test("PtVariable builder") {
|
||||
val pos = Position.DUMMY
|
||||
val variable = PtVariable.builder("x", DataType.UBYTE, pos)
|
||||
.zeropage(ZeropageWish.REQUIRE_ZEROPAGE)
|
||||
.align(2u)
|
||||
.dirty(true)
|
||||
.arraySize(10u)
|
||||
.build()
|
||||
|
||||
variable.name shouldBe "x"
|
||||
variable.type shouldBe DataType.UBYTE
|
||||
variable.zeropage shouldBe ZeropageWish.REQUIRE_ZEROPAGE
|
||||
variable.align shouldBe 2u
|
||||
variable.dirty shouldBe true
|
||||
variable.arraySize shouldBe 10u
|
||||
variable.position shouldBe pos
|
||||
}
|
||||
|
||||
test("PtAsmSub builder") {
|
||||
val pos = Position.DUMMY
|
||||
val sub = PtAsmSub.builder("my_asm_sub", pos)
|
||||
.clobbers(setOf(CpuRegister.A, CpuRegister.X))
|
||||
.inline(true)
|
||||
.addReturn(RegisterOrStatusflag(RegisterOrPair.A, null), DataType.UBYTE)
|
||||
.build()
|
||||
|
||||
sub.name shouldBe "my_asm_sub"
|
||||
sub.clobbers shouldBe setOf(CpuRegister.A, CpuRegister.X)
|
||||
sub.inline shouldBe true
|
||||
sub.returns shouldHaveSize 1
|
||||
sub.returns[0].first.registerOrPair shouldBe RegisterOrPair.A
|
||||
sub.returns[0].second shouldBe DataType.UBYTE
|
||||
}
|
||||
|
||||
test("PtConstant builder") {
|
||||
val pos = Position.DUMMY
|
||||
val constant = PtConstant.builder("MY_CONST", DataType.FLOAT, pos)
|
||||
.value(3.14)
|
||||
.build()
|
||||
|
||||
constant.name shouldBe "MY_CONST"
|
||||
constant.type shouldBe DataType.FLOAT
|
||||
constant.value shouldBe 3.14
|
||||
constant.memorySlab shouldBe null
|
||||
}
|
||||
|
||||
test("PtMemMapped builder") {
|
||||
val pos = Position.DUMMY
|
||||
val mem = PtMemMapped.builder("SCREEN", DataType.UBYTE, 0x0400u, pos)
|
||||
.arraySize(1000u)
|
||||
.build()
|
||||
|
||||
mem.name shouldBe "SCREEN"
|
||||
mem.type shouldBe DataType.UBYTE
|
||||
mem.address shouldBe 0x0400u
|
||||
mem.arraySize shouldBe 1000u
|
||||
}
|
||||
|
||||
test("PtSub builder") {
|
||||
val pos = Position.DUMMY
|
||||
val sub = PtSub.builder("my_sub", pos)
|
||||
.addReturntype(DataType.UWORD)
|
||||
.addParameter(PtSubroutineParameter("p1", DataType.UBYTE, null, pos))
|
||||
.build()
|
||||
|
||||
sub.name shouldBe "my_sub"
|
||||
sub.signature.returns shouldBe listOf(DataType.UWORD)
|
||||
sub.signature.children shouldHaveSize 1
|
||||
(sub.signature.children[0] as PtSubroutineParameter).name shouldBe "p1"
|
||||
}
|
||||
|
||||
test("PtStructDecl builder") {
|
||||
val pos = Position.DUMMY
|
||||
val struct = PtStructDecl.builder("Point", pos)
|
||||
.addField(DataType.UBYTE, "x")
|
||||
.addField(DataType.UBYTE, "y")
|
||||
.build()
|
||||
|
||||
struct.name shouldBe "Point"
|
||||
struct.fields shouldHaveSize 2
|
||||
struct.fields[0] shouldBe (DataType.UBYTE to "x")
|
||||
struct.fields[1] shouldBe (DataType.UBYTE to "y")
|
||||
}
|
||||
})
|
||||
@@ -14,20 +14,14 @@ class TestVm: FunSpec( {
|
||||
|
||||
fun getTestOptions(): CompilationOptions {
|
||||
val target = VMTarget()
|
||||
return CompilationOptions(
|
||||
OutputType.RAW,
|
||||
CbmPrgLauncherType.NONE,
|
||||
ZeropageType.DONTUSE,
|
||||
zpReserved = emptyList(),
|
||||
zpAllowed = CompilationOptions.AllZeropageAllowed,
|
||||
floats = true,
|
||||
noSysInit = false,
|
||||
romable = false,
|
||||
compTarget = target,
|
||||
compilerVersion ="99.99",
|
||||
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
||||
memtopAddress = 0xffffu
|
||||
)
|
||||
return CompilationOptions.builder(target)
|
||||
.output(OutputType.RAW)
|
||||
.zeropage(ZeropageType.DONTUSE)
|
||||
.floats(true)
|
||||
.compilerVersion("99.99")
|
||||
.loadAddress(target.PROGRAM_LOAD_ADDRESS)
|
||||
.memtopAddress(0xffffu)
|
||||
.build()
|
||||
}
|
||||
|
||||
test("vm execution: empty program") {
|
||||
|
||||
Reference in new issue
Block a user