using builder to improve massive lists of constructor params

This commit is contained in:
Irmen de Jong committed 2026-04-19 01:39:50 +02:00
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) {
+7 -14
View File
@@ -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") {
+6 -14
View File
@@ -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()
+6 -14
View File
@@ -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") {
+12 -6
View File
@@ -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) {
+166 -98
View File
@@ -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
}
+122
View File
@@ -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)
+1 -1
View File
@@ -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> {
+9 -15
View File
@@ -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()
+106 -2
View File
@@ -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")
}
})
+8 -14
View File
@@ -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") {