use VarDecl builder instead of constructor

This commit is contained in:
Irmen de Jong committed 2026-04-19 02:26:50 +02:00
1 parent 930697dad5
commit 8592a3a200
14 files changed
+229 -154

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
---
description: Startup workflow to load project context
---
1. Read the file `AGENTS.md` in the project root as initial system prompt to build context and instructions about the project structure, module descriptions, and development commands.
@@ -82,7 +82,11 @@ class VarConstantValueTypeAdjuster(
if (declValue != null) {
// variable is never written to, so it can be replaced with a constant, IF the value is a constant
errors.info("variable '${decl.name}' is never written to and was replaced by a constant", decl.position)
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.splitwordarray, decl.arraysize, decl.matrixNumCols?.copy(), decl.name, decl.names, declValue, decl.sharedWithAsm, decl.alignment, decl.dirty, decl.isPrivate, decl.position)
val const = VarDecl.builder(decl.datatype, decl.position)
.copyFrom(decl)
.type(VarDeclType.CONST)
.value(declValue)
.build()
decl.value = null
return listOf(
AstReplaceNode(decl, const, parent)
@@ -107,7 +111,11 @@ class VarConstantValueTypeAdjuster(
// variable only has a single write, and it is the initialization value, so it can be replaced with a constant, but only IF the value is a constant
errors.info("variable '${decl.name}' is never written to and was replaced by a constant", decl.position)
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.splitwordarray, decl.arraysize, decl.matrixNumCols?.copy(), decl.name, decl.names, singleAssignment.value, decl.sharedWithAsm, decl.alignment, decl.dirty, decl.isPrivate, decl.position)
val const = VarDecl.builder(decl.datatype, decl.position)
.copyFrom(decl)
.type(VarDeclType.CONST)
.value(singleAssignment.value)
.build()
return listOf(
AstReplaceNode(decl, const, parent),
AstRemove(singleAssignment, singleAssignment.parent as IStatementContainer)
@@ -449,9 +457,12 @@ internal class ConstantIdentifierReplacer(
if(range!=null) {
val targetDatatype = assignment.target.inferType(program)
if(targetDatatype.isArray) {
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.ARRAYLITERAL, targetDatatype.getOrUndef(),
ZeropageWish.DONTCARE, SplitWish.NOSPLIT, null, null, "dummy", emptyList(),
assignment.value, false, 0u, false, false, assignment.value.position)
val decl = VarDecl.builder(targetDatatype.getOrUndef(), assignment.value.position)
.names("dummy")
.origin(VarDeclOrigin.ARRAYLITERAL)
.splitwordarray(SplitWish.NOSPLIT)
.value(assignment.value)
.build()
val replaceValue = createConstArrayInitializerValue(decl)
if(replaceValue!=null) {
return listOf(AstReplaceNode(assignment.value, replaceValue, assignment))
@@ -239,8 +239,12 @@ class AstPreprocessor(val program: Program,
.zip(tuple.expressions)
.reversed()
.map { (name, value) ->
val decl = VarDecl(decl.type, decl.origin, decl.datatype, decl.zeropage, decl.splitwordarray, decl.arraysize, decl.matrixNumCols?.copy(), name, emptyList(), value, decl.sharedWithAsm, decl.alignment, decl.dirty, decl.isPrivate, decl.position)
AstInsert.after(decl, decl, parent as IStatementContainer)
val newDecl = VarDecl.builder(decl.datatype, decl.position)
.copyFrom(decl)
.names(name)
.value(value)
.build()
AstInsert.after(decl, newDecl, parent as IStatementContainer)
}
return vardecls + AstRemove(decl, parent as IStatementContainer)
}
@@ -502,9 +506,11 @@ class AstPreprocessor(val program: Program,
}
} else value++
val membervalue = NumericLiteral(enum.type, value.toDouble(), enum.position)
VarDecl(VarDeclType.CONST, VarDeclOrigin.USERCODE, dt,
ZeropageWish.DONTCARE, SplitWish.DONTCARE, null, null, membername, emptyList(), membervalue,
false, 0u, false, false, enum.position)
VarDecl.builder(dt, enum.position)
.names(membername)
.type(VarDeclType.CONST)
.value(membervalue)
.build()
}
/*
@@ -237,20 +237,11 @@ internal class StatementReorderer(
.filterIsInstance<VarDecl>()
.filter { it.origin==VarDeclOrigin.SUBROUTINEPARAM && it.name in stringParamsByNames && it.datatype!=DataType.pointer(BaseDataType.UBYTE) }
.map {
val newvar = VarDecl(it.type, it.origin, DataType.pointer(BaseDataType.UBYTE),
it.zeropage,
it.splitwordarray,
null,
null,
it.name,
emptyList(),
null,
it.sharedWithAsm,
it.alignment,
it.dirty,
it.isPrivate,
it.position
)
val newvar = VarDecl.builder(DataType.pointer(BaseDataType.UBYTE), it.position)
.copyFrom(it)
.arraysize(null)
.matrixNumCols(null)
.build()
AstReplaceNode(it, newvar, subroutine)
}
}
@@ -96,9 +96,10 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
if(value is ArrayLiteral && !(value.type istype changeDataType)) {
value = ArrayLiteral(InferredTypes.knownFor(changeDataType), value.value, value.position)
}
val newDecl = VarDecl(decl.type, decl.origin, changeDataType, decl.zeropage,
decl.splitwordarray, decl.arraysize, decl.matrixNumCols?.copy(), decl.name, decl.names,
value, decl.sharedWithAsm, decl.alignment, decl.dirty, decl.isPrivate, decl.position)
val newDecl = VarDecl.builder(changeDataType, decl.position)
.copyFrom(decl)
.value(value)
.build()
return listOf(AstReplaceNode(decl, newDecl, parent))
}
}
@@ -110,9 +111,10 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
if(value is ArrayLiteral && !(value.type istype newDt)) {
value = ArrayLiteral(InferredTypes.knownFor(newDt), value.value, value.position)
}
val newDecl = VarDecl(decl.type, decl.origin, newDt, decl.zeropage,
decl.splitwordarray, decl.arraysize, decl.matrixNumCols?.copy(), decl.name, decl.names,
value, decl.sharedWithAsm, decl.alignment, decl.dirty, decl.isPrivate, decl.position)
val newDecl = VarDecl.builder(newDt, decl.position)
.copyFrom(decl)
.value(value)
.build()
return listOf(AstReplaceNode(decl, newDecl, parent))
}
return noModifications
+29 -14
View File
@@ -196,8 +196,11 @@ class TestMemory: FunSpec({
}
fun createTestProgramForMemoryRefViaVar(address: UInt, vartype: VarDeclType): AssignTarget {
val decl = VarDecl(vartype, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE,
SplitWish.DONTCARE, null, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, 0u, false, false, Position.DUMMY)
val decl = VarDecl.builder(DataType.BYTE, Position.DUMMY)
.names("address")
.type(vartype)
.value(NumericLiteral.optimalInteger(address, Position.DUMMY))
.build()
val memexpr = IdentifierReference(listOf("address"), Position.DUMMY)
val target = AssignTarget(
null,
@@ -256,8 +259,7 @@ class TestMemory: FunSpec({
}
test("regular variable not in mapped IO ram on C64") {
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE,
SplitWish.DONTCARE, null, null, "address", emptyList(), null, false, 0u, false, false, Position.DUMMY)
val decl = VarDecl.builder(DataType.BYTE, Position.DUMMY).names("address").build()
val target = AssignTarget(
IdentifierReference(listOf("address"), Position.DUMMY),
null,
@@ -276,8 +278,11 @@ class TestMemory: FunSpec({
test("memory mapped variable not in mapped IO ram on C64") {
val address = 0x1000u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE,
SplitWish.DONTCARE, null, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, 0u, false, false, Position.DUMMY)
val decl = VarDecl.builder(DataType.UBYTE, Position.DUMMY)
.names("address")
.type(VarDeclType.MEMORY)
.value(NumericLiteral.optimalInteger(address, Position.DUMMY))
.build()
val target = AssignTarget(
IdentifierReference(listOf("address"), Position.DUMMY),
null,
@@ -296,8 +301,11 @@ class TestMemory: FunSpec({
test("memory mapped variable in mapped IO ram on C64") {
val address = 0xd020u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE,
SplitWish.DONTCARE, null, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, 0u, false, false, Position.DUMMY)
val decl = VarDecl.builder(DataType.UBYTE, Position.DUMMY)
.names("address")
.type(VarDeclType.MEMORY)
.value(NumericLiteral.optimalInteger(address, Position.DUMMY))
.build()
val target = AssignTarget(
IdentifierReference(listOf("address"), Position.DUMMY),
null,
@@ -315,8 +323,9 @@ class TestMemory: FunSpec({
}
test("array not in mapped IO ram") {
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.arrayFor(BaseDataType.UBYTE), ZeropageWish.DONTCARE,
SplitWish.DONTCARE, null, null, "address", emptyList(), null, false, 0u, false, false, Position.DUMMY)
val decl = VarDecl.builder(DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY)
.names("address")
.build()
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), null, null, ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, null, false, position = Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
@@ -329,8 +338,11 @@ class TestMemory: FunSpec({
test("memory mapped array not in mapped IO ram") {
val address = 0x1000u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.arrayFor(BaseDataType.UBYTE), ZeropageWish.DONTCARE,
SplitWish.DONTCARE, null, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, 0u, false, false, Position.DUMMY)
val decl = VarDecl.builder(DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY)
.names("address")
.type(VarDeclType.MEMORY)
.value(NumericLiteral.optimalInteger(address, Position.DUMMY))
.build()
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), null, null, ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, null, false, position = Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
@@ -343,8 +355,11 @@ class TestMemory: FunSpec({
test("memory mapped array in mapped IO ram") {
val address = 0xd800u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.arrayFor(BaseDataType.UBYTE), ZeropageWish.DONTCARE,
SplitWish.DONTCARE, null, null, "address", emptyList(), NumericLiteral.optimalInteger(address, Position.DUMMY), false, 0u, false, false, Position.DUMMY)
val decl = VarDecl.builder(DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY)
.names("address")
.type(VarDeclType.MEMORY)
.value(NumericLiteral.optimalInteger(address, Position.DUMMY))
.build()
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), null, null, ArrayIndex(NumericLiteral.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, arrayindexed, null, null, false, position = Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
+17 -2
View File
@@ -393,7 +393,14 @@ class TestCx16Zeropage: FunSpec({
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))
val zp = CX16Zeropage(CompilationOptions.builder(cx16target)
.output(OutputType.RAW)
.zeropage(ZeropageType.FULL)
.compilerVersion("99.99")
.loadAddress(999u)
.memtopAddress(0xffffu)
.build()
)
zp.availableBytes() shouldBe 214
// LONG requires 4 sequential bytes
val result = zp.allocate("", DataType.LONG, null, null, errors)
@@ -406,7 +413,15 @@ class TestCx16Zeropage: FunSpec({
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))
val zp = CX16Zeropage(CompilationOptions.builder(cx16target)
.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)
+37 -9
View File
@@ -34,6 +34,34 @@ class TestBuilders : FunSpec({
decl.names shouldBe listOf("v1", "v2", "v3")
}
test("VarDecl builder list names") {
val decl = VarDecl.builder(DataType.UBYTE, pos)
.names(listOf("v1", "v2"))
.build()
decl.name shouldBe "<multiple>"
decl.names shouldBe listOf("v1", "v2")
}
test("VarDecl builder copyFrom") {
val original = VarDecl.builder(DataType.UBYTE, pos)
.names("v1")
.type(VarDeclType.CONST)
.zeropage(ZeropageWish.REQUIRE_ZEROPAGE)
.value(NumericLiteral.optimalInteger(42, pos))
.build()
val copy = VarDecl.builder(DataType.UWORD, pos)
.copyFrom(original)
.build()
copy.datatype shouldBe DataType.UWORD
copy.name shouldBe "v1"
copy.type shouldBe VarDeclType.CONST
copy.zeropage shouldBe ZeropageWish.REQUIRE_ZEROPAGE
(copy.value as NumericLiteral).number shouldBe 42.0
}
test("VarDecl builder advanced properties") {
val valExpr = NumericLiteral.optimalInteger(42, pos)
val numColsExpr = NumericLiteral.optimalInteger(10, pos)
@@ -41,17 +69,17 @@ class TestBuilders : FunSpec({
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)
.dirty(true)
.isPrivate(true)
.matrixNumCols(numColsExpr)
.origin(VarDeclOrigin.ARRAYLITERAL)
.sharedWithAsm(true)
.splitwordarray(SplitWish.NOSPLIT)
.type(VarDeclType.CONST)
.value(valExpr)
.zeropage(ZeropageWish.REQUIRE_ZEROPAGE)
.build()
decl.name shouldBe "myconst"
@@ -50,10 +50,8 @@ class TestAsmGenSymbols: StringSpec({
}
*/
val varInSub = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE,
SplitWish.DONTCARE, null, null, "localvar", emptyList(), null, false, 0u, false, false, Position.DUMMY)
val var2InSub = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE,
SplitWish.DONTCARE, null, null, "tgt", emptyList(), null, false, 0u, false, false, Position.DUMMY)
val varInSub = VarDecl.builder(DataType.UWORD, Position.DUMMY).names("localvar").build()
val var2InSub = VarDecl.builder(DataType.UWORD, Position.DUMMY).names("tgt").build()
val labelInSub = Label("locallabel", Position.DUMMY)
val tgt = AssignTarget(
@@ -76,8 +74,7 @@ class TestAsmGenSymbols: StringSpec({
val statements = mutableListOf(varInSub, var2InSub, labelInSub, assign1, assign2, assign3, assign4, assign5, assign6, assign7, assign8)
val subroutine = Subroutine("start", mutableListOf(), mutableListOf(), emptyList(), emptyList(), emptySet(), null, false, false, false, false, statements, Position.DUMMY)
val labelInBlock = Label("label_outside", Position.DUMMY)
val varInBlock = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.UWORD, ZeropageWish.DONTCARE,
SplitWish.DONTCARE, null, null, "var_outside", emptyList(),null, false, 0u, false, false, Position.DUMMY)
val varInBlock = VarDecl.builder(DataType.UWORD, Position.DUMMY).names("var_outside").build()
val block = Block("main", null, mutableListOf(labelInBlock, varInBlock, subroutine), false, Position.DUMMY)
val module = Module(mutableListOf(block), Position.DUMMY, SourceCode.Generated("test"))
@@ -89,12 +86,12 @@ class TestAsmGenSymbols: StringSpec({
fun createTestAsmGen6502(program: Program): AsmGen6502Internal {
val errors = ErrorReporterForTests()
val options = CompilationOptions.builder(C64Target())
.output(OutputType.RAW)
.zeropage(ZeropageType.FULL)
.noSysInit(true)
.compilerVersion("99.99")
.loadAddress(999u)
.memtopAddress(0xffffu)
.noSysInit(true)
.output(OutputType.RAW)
.zeropage(ZeropageType.FULL)
.build()
val astchecker = AstChecker(program, errors, options)
astchecker.visit(program)
+6 -5
View File
@@ -89,11 +89,12 @@ class Program(val name: String,
fun addNewInternedStringvar(string: StringLiteral): Pair<List<String>, VarDecl> {
val varName = "string_${internedStringsBlock.statements.size}"
val decl = VarDecl(
VarDeclType.VAR, VarDeclOrigin.STRINGLITERAL, DataType.STR, ZeropageWish.NOT_IN_ZEROPAGE,
SplitWish.DONTCARE, null, null, varName, emptyList(), string,
sharedWithAsm = false, alignment = 0u, dirty = false, isPrivate = false, position = string.position
)
val decl = VarDecl.builder(DataType.STR, string.position)
.names(varName)
.origin(VarDeclOrigin.STRINGLITERAL)
.value(string)
.zeropage(ZeropageWish.NOT_IN_ZEROPAGE)
.build()
internedStringsBlock.statements.add(decl)
decl.linkParents(internedStringsBlock)
return Pair(listOf(INTERNED_STRINGS_MODULENAME, decl.name), decl)
@@ -186,8 +186,6 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
throw SyntaxError("choose a single alignment option", ctx.toPosition())
val identifiers = ctx.identifierlist().identifier().map { getname(it) }
val identifiername = identifiers[0]
val name = if(identifiers.size==1) identifiername else "<multiple>"
// Handle 0, 1, or 2 array indices
val arrayIndices = ctx.arrayindex()
@@ -223,23 +221,17 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
DataType.arrayFor(baseDt.base, split!=SplitWish.NOSPLIT)
}
return VarDecl(
VarDeclType.VAR, // can be changed to MEMORY or CONST as required
VarDeclOrigin.USERCODE,
dt,
zp,
split,
arraySize,
matrixNumCols,
name,
if(identifiers.size==1) emptyList() else identifiers,
null,
"@shared" in tags,
if(alignword) 2u else if(align64) 64u else if(alignpage) 256u else 0u,
"@dirty" in tags,
isPrivate,
ctx.toPosition()
)
return VarDecl.builder(dt, ctx.toPosition())
.names(identifiers)
.alignment(if(alignword) 2u else if(align64) 64u else if(alignpage) 256u else 0u)
.arraysize(arraySize)
.dirty("@dirty" in tags)
.isPrivate(isPrivate)
.matrixNumCols(matrixNumCols)
.sharedWithAsm("@shared" in tags)
.splitwordarray(split)
.zeropage(zp)
.build()
}
override fun visitVarinitializer(ctx: VarinitializerContext): VarDecl {
@@ -258,31 +250,18 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
val isPrivate = ctx.PRIVATE() != null
val datatype = dataTypeFor(ctx.datatype()) ?: DataType.LONG
val identifiers = ctx.identifierlist().identifier().map { getname(it) }
val identifiername = identifiers[0]
val name = if(identifiers.size==1) identifiername else "<multiple>"
val initialvalue = ctx.expression().accept(this) as Expression
val actualValue = if(initialvalue is NumericLiteral && datatype.base.largerSizeThan(initialvalue.type))
NumericLiteral(datatype.base, initialvalue.number, initialvalue.position)
else
initialvalue
return VarDecl(
VarDeclType.CONST,
VarDeclOrigin.USERCODE,
datatype,
ZeropageWish.DONTCARE,
SplitWish.DONTCARE,
null,
null,
name,
if(identifiers.size==1) emptyList() else identifiers,
actualValue,
false,
0u,
false,
isPrivate,
ctx.toPosition()
)
return VarDecl.builder(datatype, ctx.toPosition())
.names(identifiers)
.isPrivate(isPrivate)
.type(VarDeclType.CONST)
.value(actualValue)
.build()
}
override fun visitMemoryvardecl(ctx: MemoryvardeclContext): VarDecl {
@@ -298,6 +298,11 @@ class VarDecl(
this.name = if(names.size > 1) "<multiple>" else names[0]
this.additionalNames = if(names.size > 1) names.toList() else emptyList()
}
fun names(names: List<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 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 }
@@ -310,6 +315,22 @@ class VarDecl(
fun dirty(d: Boolean) = apply { this.dirty = d }
fun isPrivate(p: Boolean) = apply { this.isPrivate = p }
fun copyFrom(v: VarDecl) = apply {
this.type = v.type
this.origin = v.origin
this.zeropage = v.zeropage
this.splitwordarray = v.splitwordarray
this.arraysize = v.arraysize
this.matrixNumCols = v.matrixNumCols
this.name = v.name
this.additionalNames = v.names
this.value = v.value
this.sharedWithAsm = v.sharedWithAsm
this.alignment = v.alignment
this.dirty = v.dirty
this.isPrivate = v.isPrivate
}
fun build(): VarDecl {
val finalName = name ?: throw IllegalStateException("name is required")
return VarDecl(
@@ -337,13 +358,13 @@ class VarDecl(
param.type.isString -> DataType.pointer(BaseDataType.UBYTE)
else -> param.type
}
return VarDecl(decltype, VarDeclOrigin.SUBROUTINEPARAM, dt, param.zp, SplitWish.DONTCARE, null, null, param.name, emptyList(), value,
sharedWithAsm = false,
alignment = 0u,
dirty = false,
isPrivate = false,
position = param.position
)
return builder(dt, param.position)
.names(param.name)
.origin(VarDeclOrigin.SUBROUTINEPARAM)
.type(decltype)
.value(value)
.zeropage(param.zp)
.build()
}
fun createAuto(array: ArrayLiteral): VarDecl {
@@ -358,9 +379,14 @@ class VarDecl(
}
}
val arraysize = ArrayIndex.forArray(array)
return VarDecl(VarDeclType.VAR, VarDeclOrigin.ARRAYLITERAL, arrayDt, ZeropageWish.NOT_IN_ZEROPAGE,
SplitWish.NOSPLIT, arraysize, null, autoVarName, emptyList(), array,
sharedWithAsm = false, alignment = 0u, dirty = false, isPrivate = false, position = array.position)
return builder(arrayDt, array.position)
.names(autoVarName)
.arraysize(arraysize)
.origin(VarDeclOrigin.ARRAYLITERAL)
.splitwordarray(SplitWish.NOSPLIT)
.value(array)
.zeropage(ZeropageWish.NOT_IN_ZEROPAGE)
.build()
}
fun createAutoOptionalSplit(array: ArrayLiteral): VarDecl {
@@ -368,17 +394,21 @@ class VarDecl(
val arrayDt = array.type.getOrElse { throw FatalAstException("unknown dt") }
val split = if(arrayDt.isSplitWordArray) SplitWish.DONTCARE else if(arrayDt.isWordArray) SplitWish.NOSPLIT else SplitWish.DONTCARE
val arraysize = ArrayIndex.forArray(array)
return VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, arrayDt, ZeropageWish.NOT_IN_ZEROPAGE,
split, arraysize, null, autoVarName, emptyList(), array,
sharedWithAsm = false, alignment = 0u, dirty = false, isPrivate = false, position = array.position)
return builder(arrayDt, array.position)
.names(autoVarName)
.arraysize(arraysize)
.splitwordarray(split)
.value(array)
.zeropage(ZeropageWish.NOT_IN_ZEROPAGE)
.build()
}
fun createAuto(dt: DataType, position: Position): VarDecl {
val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}"
val vardecl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, dt, ZeropageWish.NOT_IN_ZEROPAGE,
SplitWish.DONTCARE, null, null, autoVarName, emptyList(), null,
sharedWithAsm = false, alignment = 0u, dirty = false, isPrivate = false, position = position)
return vardecl
return builder(dt, position)
.names(autoVarName)
.zeropage(ZeropageWish.NOT_IN_ZEROPAGE)
.build()
}
}
@@ -417,8 +447,7 @@ class VarDecl(
fun copy(newDatatype: DataType): VarDecl {
if(names.size>1)
throw FatalAstException("should not copy a vardecl that still has multiple names")
val copy = VarDecl(type, origin, newDatatype, zeropage, splitwordarray, arraysize?.copy(), matrixNumCols?.copy(), name, names, value?.copy(),
sharedWithAsm, alignment, dirty, isPrivate, position)
val copy = builder(newDatatype, position).copyFrom(this).build()
copy.allowInitializeWithZero = this.allowInitializeWithZero
return copy
}
@@ -433,20 +462,17 @@ class VarDecl(
if(value==null || value?.isSimple==true) {
// just copy the initialization value to a separate vardecl for each component
return names.map {
val copy = VarDecl(type, origin, datatype, zeropage, splitwordarray, arraysize?.copy(), matrixNumCols?.copy(), it, emptyList(), value?.copy(),
sharedWithAsm, alignment, dirty, isPrivate, position)
val copy = builder(datatype, position).copyFrom(this).names(it).value(value?.copy()).build()
copy.allowInitializeWithZero = this.allowInitializeWithZero
copy
}
} else {
// evaluate the value once in the vardecl for the first component, and set the other components to the first
val first = VarDecl(type, origin, datatype, zeropage, splitwordarray, arraysize?.copy(), matrixNumCols?.copy(), names[0], emptyList(), value?.copy(),
sharedWithAsm, alignment, dirty, isPrivate, position)
val first = builder(datatype, position).copyFrom(this).names(names[0]).value(value?.copy()).build()
first.allowInitializeWithZero = this.allowInitializeWithZero
val firstVar = firstVarAsValue(first)
return listOf(first) + names.drop(1 ).map {
val copy = VarDecl(type, origin, datatype, zeropage, splitwordarray, arraysize?.copy(), matrixNumCols?.copy(), it, emptyList(), firstVar.copy(),
sharedWithAsm, alignment, dirty, isPrivate, position)
val copy = builder(datatype, position).copyFrom(this).names(it).value(firstVar.copy()).build()
copy.allowInitializeWithZero = this.allowInitializeWithZero
copy
}
@@ -462,6 +488,7 @@ 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
}
@@ -511,6 +538,7 @@ 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
}
@@ -1102,8 +1130,8 @@ class Subroutine(override val name: String,
val isPrivate: Boolean,
override val statements: MutableList<Statement>,
override val position: Position) : Statement(), INameScope {
override lateinit var parent: Node
override fun copy() = throw NotImplementedError("no support for duplicating a Subroutine")
override fun linkParents(parent: Node) {
@@ -1527,8 +1555,8 @@ class OnGoto(
val elsepart: AnonymousScope?,
override val position: Position
) : Statement() {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
this.parent = parent
index.linkParents(this)
-1
View File
@@ -22,7 +22,6 @@ Weird Heisenbug
Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^
- 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
+17 -19
View File
@@ -28,9 +28,7 @@ object SymbolLookup {
SUBROUTINE,
BLOCK,
STRUCT,
STRUCT_FIELD,
PARAMETER,
UNKNOWN
PARAMETER
}
/**
@@ -81,7 +79,7 @@ object SymbolLookup {
* Handles nested subroutines at any depth.
*/
private fun collectSymbols(
stmt: prog8.ast.statements.Statement,
stmt: Statement,
blocks: MutableMap<String, SymbolAtPosition>,
subroutines: MutableMap<String, SymbolAtPosition>
) {
@@ -333,7 +331,7 @@ object SymbolLookup {
}
private fun findReferenceInExpression(
expr: prog8.ast.expressions.Expression,
expr: Expression,
targetLine: Int,
targetCol: Int,
wordAtCursor: String,
@@ -342,7 +340,7 @@ object SymbolLookup {
subroutines: Map<String, SymbolAtPosition>
): SymbolAtPosition? {
return when (expr) {
is prog8.ast.expressions.IdentifierReference -> {
is IdentifierReference -> {
val identName = expr.nameInSource.lastOrNull() ?: ""
if (isPositionOnName(expr.position, targetLine, targetCol, identName, wordAtCursor)) {
// Simple unscoped lookup: check vars first, then blocks, then subroutines
@@ -356,11 +354,11 @@ object SymbolLookup {
}
null
}
is prog8.ast.expressions.BinaryExpression -> {
is BinaryExpression -> {
findReferenceInExpression(expr.left, targetLine, targetCol, wordAtCursor, visibleVars, blocks, subroutines)
?: findReferenceInExpression(expr.right, targetLine, targetCol, wordAtCursor, visibleVars, blocks, subroutines)
}
is prog8.ast.expressions.FunctionCallExpression -> {
is FunctionCallExpression -> {
// Check function name (handles qualified names)
val funcName = expr.target.nameInSource.lastOrNull() ?: ""
if (isPositionOnName(expr.target.position, targetLine, targetCol, funcName, wordAtCursor)) {
@@ -439,7 +437,7 @@ object SymbolLookup {
return null
}
private fun isPositionOnName(position: prog8.code.core.Position, targetLine: Int, targetCol: Int, name: String, wordAtCursor: String): Boolean {
private fun isPositionOnName(position: Position, targetLine: Int, targetCol: Int, name: String, wordAtCursor: String): Boolean {
if (position.line != targetLine) return false
// Check if column is within the name's column range
if (targetCol < position.startCol || targetCol > position.endCol) return false
@@ -718,7 +716,7 @@ object SymbolLookup {
}
private fun findReferencesInExpressionScoped(
expr: prog8.ast.expressions.Expression,
expr: Expression,
targetSymbol: SymbolAtPosition,
locations: MutableList<org.eclipse.lsp4j.Location>,
uri: String,
@@ -727,21 +725,21 @@ object SymbolLookup {
subroutines: Map<String, SymbolAtPosition>
) {
when (expr) {
is prog8.ast.expressions.IdentifierReference -> {
is IdentifierReference -> {
val identName = expr.nameInSource.lastOrNull() ?: ""
val resolved = resolveIdentifierScoped(identName, visibleVars, blocks, subroutines)
if (resolved?.definitionPosition == targetSymbol.definitionPosition) {
locations.add(expr.position.toLspLocation(uri))
}
}
is prog8.ast.expressions.BinaryExpression -> {
is BinaryExpression -> {
findReferencesInExpressionScoped(expr.left, targetSymbol, locations, uri, visibleVars, blocks, subroutines)
findReferencesInExpressionScoped(expr.right, targetSymbol, locations, uri, visibleVars, blocks, subroutines)
}
is prog8.ast.expressions.PrefixExpression -> {
is PrefixExpression -> {
findReferencesInExpressionScoped(expr.expression, targetSymbol, locations, uri, visibleVars, blocks, subroutines)
}
is prog8.ast.expressions.FunctionCallExpression -> {
is FunctionCallExpression -> {
val funcName = expr.target.nameInSource.lastOrNull() ?: ""
val resolved = resolveIdentifierScoped(funcName, visibleVars, blocks, subroutines)
if (resolved?.definitionPosition == targetSymbol.definitionPosition) {
@@ -751,15 +749,15 @@ object SymbolLookup {
findReferencesInExpressionScoped(arg, targetSymbol, locations, uri, visibleVars, blocks, subroutines)
}
}
is prog8.ast.expressions.TypecastExpression -> {
is TypecastExpression -> {
findReferencesInExpressionScoped(expr.expression, targetSymbol, locations, uri, visibleVars, blocks, subroutines)
}
is prog8.ast.expressions.IfExpression -> {
is IfExpression -> {
findReferencesInExpressionScoped(expr.condition, targetSymbol, locations, uri, visibleVars, blocks, subroutines)
findReferencesInExpressionScoped(expr.truevalue, targetSymbol, locations, uri, visibleVars, blocks, subroutines)
findReferencesInExpressionScoped(expr.falsevalue, targetSymbol, locations, uri, visibleVars, blocks, subroutines)
}
is prog8.ast.expressions.ArrayIndexedExpression -> {
is ArrayIndexedExpression -> {
expr.plainarrayvar?.let {
val resolved = resolveIdentifierScoped(it.nameInSource.lastOrNull() ?: "", visibleVars, blocks, subroutines)
if (resolved?.definitionPosition == targetSymbol.definitionPosition) {
@@ -769,10 +767,10 @@ object SymbolLookup {
expr.nestedArray?.let { findReferencesInExpressionScoped(it, targetSymbol, locations, uri, visibleVars, blocks, subroutines) }
findReferencesInExpressionScoped(expr.indexer.indexExpr, targetSymbol, locations, uri, visibleVars, blocks, subroutines)
}
is prog8.ast.expressions.RangeExpression -> {
is RangeExpression -> {
findReferencesInExpressionScoped(expr.from, targetSymbol, locations, uri, visibleVars, blocks, subroutines)
findReferencesInExpressionScoped(expr.to, targetSymbol, locations, uri, visibleVars, blocks, subroutines)
expr.step?.let { findReferencesInExpressionScoped(it, targetSymbol, locations, uri, visibleVars, blocks, subroutines) }
expr.step.let { findReferencesInExpressionScoped(it, targetSymbol, locations, uri, visibleVars, blocks, subroutines) }
}
else -> {}
}