mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
use VarDecl builder instead of constructor
This commit is contained in:
1 parent
930697dad5
commit
8592a3a200
14 files changed
+229
-154
No files matched your search
@@ -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
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 -> {}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user