diff --git a/.junie/AGENTS.md b/.junie/AGENTS.md new file mode 100644 index 000000000..e088a3bc6 --- /dev/null +++ b/.junie/AGENTS.md @@ -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. diff --git a/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt b/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt index 7fefb53f9..1de01c90d 100644 --- a/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt +++ b/codeOptimizers/src/prog8/optimizer/ConstantIdentifierReplacer.kt @@ -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)) diff --git a/compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt b/compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt index cd8d6fa7d..93fffd314 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt @@ -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() } /* diff --git a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt index 87dc06a8b..e9bb9f1d7 100644 --- a/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt +++ b/compiler/src/prog8/compiler/astprocessing/StatementReorderer.kt @@ -237,20 +237,11 @@ internal class StatementReorderer( .filterIsInstance() .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) } } diff --git a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt index 46003b9ee..5a1a149d6 100644 --- a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt +++ b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt @@ -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 diff --git a/compiler/test/TestMemory.kt b/compiler/test/TestMemory.kt index 8d39e8a17..d8e24b96e 100644 --- a/compiler/test/TestMemory.kt +++ b/compiler/test/TestMemory.kt @@ -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) diff --git a/compiler/test/TestZeropageAllocation.kt b/compiler/test/TestZeropageAllocation.kt index 1180043a7..889526719 100644 --- a/compiler/test/TestZeropageAllocation.kt +++ b/compiler/test/TestZeropageAllocation.kt @@ -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) diff --git a/compiler/test/ast/TestBuilders.kt b/compiler/test/ast/TestBuilders.kt index 4bbcd2ec6..b7501f112 100644 --- a/compiler/test/ast/TestBuilders.kt +++ b/compiler/test/ast/TestBuilders.kt @@ -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 "" + 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" diff --git a/compiler/test/codegeneration/TestAsmGenSymbols.kt b/compiler/test/codegeneration/TestAsmGenSymbols.kt index 78d592cff..4d95a630a 100644 --- a/compiler/test/codegeneration/TestAsmGenSymbols.kt +++ b/compiler/test/codegeneration/TestAsmGenSymbols.kt @@ -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) diff --git a/compilerAst/src/prog8/ast/Program.kt b/compilerAst/src/prog8/ast/Program.kt index 99e77c6f6..9cc4c470d 100644 --- a/compilerAst/src/prog8/ast/Program.kt +++ b/compilerAst/src/prog8/ast/Program.kt @@ -89,11 +89,12 @@ class Program(val name: String, fun addNewInternedStringvar(string: StringLiteral): Pair, 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) diff --git a/compilerAst/src/prog8/ast/antlr/Antlr2KotlinVisitor.kt b/compilerAst/src/prog8/ast/antlr/Antlr2KotlinVisitor.kt index 649f6edfe..464a004e1 100644 --- a/compilerAst/src/prog8/ast/antlr/Antlr2KotlinVisitor.kt +++ b/compilerAst/src/prog8/ast/antlr/Antlr2KotlinVisitor.kt @@ -186,8 +186,6 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor" // Handle 0, 1, or 2 array indices val arrayIndices = ctx.arrayindex() @@ -223,23 +221,17 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor" 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 { diff --git a/compilerAst/src/prog8/ast/statements/AstStatements.kt b/compilerAst/src/prog8/ast/statements/AstStatements.kt index 7035efdae..93fb32691 100644 --- a/compilerAst/src/prog8/ast/statements/AstStatements.kt +++ b/compilerAst/src/prog8/ast/statements/AstStatements.kt @@ -298,6 +298,11 @@ class VarDecl( this.name = if(names.size > 1) "" else names[0] this.additionalNames = if(names.size > 1) names.toList() else emptyList() } + fun names(names: List): Builder = apply { + require(names.isNotEmpty()) { "at least one name is required" } + this.name = if(names.size > 1) "" 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>, 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>, 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, 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) diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 68ca10090..56f0a92a8 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -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 diff --git a/languageServer/src/prog8lsp/SymbolLookup.kt b/languageServer/src/prog8lsp/SymbolLookup.kt index c98d8f6f4..8d706e41d 100644 --- a/languageServer/src/prog8lsp/SymbolLookup.kt +++ b/languageServer/src/prog8lsp/SymbolLookup.kt @@ -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, subroutines: MutableMap ) { @@ -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 ): 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, uri: String, @@ -727,21 +725,21 @@ object SymbolLookup { subroutines: Map ) { 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 -> {} }