diff --git a/codeCore/src/prog8/code/SymbolTable.kt b/codeCore/src/prog8/code/SymbolTable.kt index 20a5948c2..d55776df0 100644 --- a/codeCore/src/prog8/code/SymbolTable.kt +++ b/codeCore/src/prog8/code/SymbolTable.kt @@ -1,6 +1,7 @@ package prog8.code import prog8.code.ast.PtNode +import prog8.code.ast.PtProgram import prog8.code.core.* @@ -8,7 +9,7 @@ import prog8.code.core.* * Tree structure containing all symbol definitions in the program * (blocks, subroutines, variables (all types), memoryslabs, and labels). */ -class SymbolTable(astNode: PtNode) : StNode("", StNodeType.GLOBAL, Position.DUMMY, astNode) { +class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GLOBAL, astProgram) { /** * The table as a flat mapping of scoped names to the StNode. * This gives the fastest lookup possible (no need to traverse tree nodes) @@ -77,7 +78,6 @@ enum class StNodeType { open class StNode(val name: String, val type: StNodeType, - val position: Position, val astNode: PtNode, val children: MutableMap = mutableMapOf() ) { @@ -153,8 +153,7 @@ class StStaticVariable(name: String, val onetimeInitializationArrayValue: StArray?, val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte val zpwish: ZeropageWish, // used in the variable allocator - astNode: PtNode, - position: Position) : StNode(name, StNodeType.STATICVAR, position, astNode = astNode) { + astNode: PtNode) : StNode(name, StNodeType.STATICVAR, astNode) { init { if(bss) { @@ -183,8 +182,8 @@ class StStaticVariable(name: String, } -class StConstant(name: String, val dt: DataType, val value: Double, astNode: PtNode, position: Position) : - StNode(name, StNodeType.CONSTANT, position, astNode) { +class StConstant(name: String, val dt: DataType, val value: Double, astNode: PtNode) : + StNode(name, StNodeType.CONSTANT, astNode) { } @@ -192,23 +191,21 @@ class StMemVar(name: String, val dt: DataType, val address: UInt, val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte - astNode: PtNode, - position: Position) : - StNode(name, StNodeType.MEMVAR, position, astNode) { + astNode: PtNode) : + StNode(name, StNodeType.MEMVAR, astNode) { } class StMemorySlab( name: String, val size: UInt, val align: UInt, - astNode: PtNode, - position: Position + astNode: PtNode ): - StNode(name, StNodeType.MEMORYSLAB, position, astNode) { + StNode(name, StNodeType.MEMORYSLAB, astNode) { } -class StSub(name: String, val parameters: List, val returnType: DataType?, astNode: PtNode, position: Position) : - StNode(name, StNodeType.SUBROUTINE, position, astNode) { +class StSub(name: String, val parameters: List, val returnType: DataType?, astNode: PtNode) : + StNode(name, StNodeType.SUBROUTINE, astNode) { } @@ -216,9 +213,8 @@ class StRomSub(name: String, val address: UInt, val parameters: List, val returns: List, - astNode: PtNode, - position: Position) : - StNode(name, StNodeType.ROMSUB, position, astNode) + astNode: PtNode) : + StNode(name, StNodeType.ROMSUB, astNode) diff --git a/codeCore/src/prog8/code/SymbolTableMaker.kt b/codeCore/src/prog8/code/SymbolTableMaker.kt index 233b6ee52..26e4c2adb 100644 --- a/codeCore/src/prog8/code/SymbolTableMaker.kt +++ b/codeCore/src/prog8/code/SymbolTableMaker.kt @@ -10,7 +10,7 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp val st = SymbolTable(program) BuiltinFunctions.forEach { - st.add(StNode(it.key, StNodeType.BUILTINFUNC, Position.DUMMY, PtIdentifier(it.key, it.value.returnType ?: DataType.UNDEFINED, Position.DUMMY))) + st.add(StNode(it.key, StNodeType.BUILTINFUNC, PtIdentifier(it.key, it.value.returnType ?: DataType.UNDEFINED, Position.DUMMY))) } val scopestack = Stack() @@ -29,7 +29,7 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp PtMemMapped("P8ESTACK_LO", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_LO, 256u, Position.DUMMY), PtMemMapped("P8ESTACK_HI", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_HI, 256u, Position.DUMMY) ).forEach { - st.add(StMemVar(it.name, it.type, it.address, null, it, Position.DUMMY)) + st.add(StMemVar(it.name, it.type, it.address, null, it)) } } @@ -41,28 +41,28 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp is PtAsmSub -> { if(node.address==null) { val params = node.parameters.map { StSubroutineParameter(it.second.name, it.second.type) } - StSub(node.name, params, node.returns.singleOrNull()?.second, node, node.position) + StSub(node.name, params, node.returns.singleOrNull()?.second, node) } else { val parameters = node.parameters.map { StRomSubParameter(it.first, it.second.type) } val returns = node.returns.map { StRomSubParameter(it.first, it.second) } - StRomSub(node.name, node.address, parameters, returns, node, node.position) + StRomSub(node.name, node.address, parameters, returns, node) } } is PtBlock -> { - StNode(node.name, StNodeType.BLOCK, node.position, node) + StNode(node.name, StNodeType.BLOCK, node) } is PtConstant -> { - StConstant(node.name, node.type, node.value, node, node.position) + StConstant(node.name, node.type, node.value, node) } is PtLabel -> { - StNode(node.name, StNodeType.LABEL, node.position, node) + StNode(node.name, StNodeType.LABEL, node) } is PtMemMapped -> { - StMemVar(node.name, node.type, node.address, node.arraySize?.toInt(), node, node.position) + StMemVar(node.name, node.type, node.address, node.arraySize?.toInt(), node) } is PtSub -> { val params = node.parameters.map {StSubroutineParameter(it.name, it.type) } - StSub(node.name, params, node.returntype, node, node.position) + StSub(node.name, params, node.returntype, node) } is PtVariable -> { val bss = when (node.type) { @@ -103,7 +103,7 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp numElements = node.arraySize?.toInt() } val zeropage = ZeropageWish.DONTCARE // TODO how, can this be removed from the ST perhaps? Or is it required in the variable allocator later - StStaticVariable(node.name, node.type, bss, initialNumeric, initialString, initialArray, numElements, zeropage, node, node.position) + StStaticVariable(node.name, node.type, bss, initialNumeric, initialString, initialArray, numElements, zeropage, node) } is PtBuiltinFunctionCall -> { if(node.name=="memory") { @@ -113,7 +113,7 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp val size = (node.args[1] as PtNumber).number.toUInt() val align = (node.args[2] as PtNumber).number.toUInt() // don't add memory slabs in nested scope, just put them in the top level of the ST - scope.firstElement().add(StMemorySlab("prog8_memoryslab_$slabname", size, align, node, node.position)) + scope.firstElement().add(StMemorySlab("prog8_memoryslab_$slabname", size, align, node)) } null } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt index 0680f7611..50083effb 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt @@ -59,7 +59,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable, variable.scopedName.split('.'), variable.dt, variable.length, - variable.position, + variable.astNode.position, errors ) result.fold( @@ -67,7 +67,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable, numVariablesAllocatedInZP++ }, failure = { - errors.err(it.message!!, variable.position) + errors.err(it.message!!, variable.astNode.position) } ) } @@ -78,7 +78,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable, variable.scopedName.split('.'), variable.dt, variable.length, - variable.position, + variable.astNode.position, errors ) result.onSuccess { numVariablesAllocatedInZP++ } @@ -97,7 +97,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable, variable.scopedName.split('.'), variable.dt, variable.length, - variable.position, + variable.astNode.position, errors ) result.onSuccess { numVariablesAllocatedInZP++ } diff --git a/compiler/test/TestSymbolTable.kt b/compiler/test/TestSymbolTable.kt index 563815928..2aaf289ea 100644 --- a/compiler/test/TestSymbolTable.kt +++ b/compiler/test/TestSymbolTable.kt @@ -1,17 +1,29 @@ package prog8tests -// TODO new unit tests for Symbol Table on new Ast Nodes +import io.kotest.assertions.fail +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe +import io.kotest.matchers.types.shouldBeSameInstanceAs +import prog8.code.* +import prog8.code.ast.* +import prog8.code.core.DataType +import prog8.code.core.Position +import prog8.code.core.SourceCode +import prog8.code.core.ZeropageWish +import prog8tests.helpers.DummyMemsizer +import prog8tests.helpers.DummyStringEncoder -/* class TestSymbolTable: FunSpec({ test("empty symboltable") { - val st = SymbolTable() - st.scopedName shouldBe "" - st.name shouldBe "" + val astNode = PtProgram("test", DummyMemsizer, DummyStringEncoder) + val st = SymbolTable(astNode) + st.name shouldBe "test" st.type shouldBe StNodeType.GLOBAL st.children shouldBe mutableMapOf() - st.position shouldBe Position.DUMMY + st.astNode shouldBeSameInstanceAs astNode + st.astNode.position shouldBe Position.DUMMY } test("symboltable flatten") { @@ -28,9 +40,9 @@ class TestSymbolTable: FunSpec({ st.lookupUnscoped("undefined") shouldBe null st.lookup("undefined") shouldBe null st.lookup("undefined.undefined") shouldBe null - var default = st.lookupUnscopedOrElse("undefined") { StNode("default", StNodeType.LABEL, Position.DUMMY) } + var default = st.lookupUnscopedOrElse("undefined") { StNode("default", StNodeType.LABEL, PtIdentifier("default", DataType.BYTE, Position.DUMMY)) } default.name shouldBe "default" - default = st.lookupUnscopedOrElse("undefined") { StNode("default", StNodeType.LABEL, Position.DUMMY) } + default = st.lookupUnscopedOrElse("undefined") { StNode("default", StNodeType.LABEL, PtIdentifier("default", DataType.BYTE, Position.DUMMY)) } default.name shouldBe "default" val msbFunc = st.lookupUnscopedOrElse("msb") { fail("msb must be found") } @@ -62,35 +74,73 @@ class TestSymbolTable: FunSpec({ subsub.lookupUnscoped("blockc") shouldBe null subsub.lookupUnscoped("label") shouldNotBe null } + + // TODO add more SymbolTable tests + }) private fun makeSt(): SymbolTable { - val st = SymbolTable() - val block1 = StNode("block1", StNodeType.BLOCK, Position.DUMMY) - val sub11 = StNode("sub1", StNodeType.SUBROUTINE, Position.DUMMY) - val sub12 = StNode("sub2", StNodeType.SUBROUTINE, Position.DUMMY) + + // first build the AST + val astProgram = PtProgram("test", DummyMemsizer, DummyStringEncoder) + val astBlock1 = PtBlock("block1", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("block1"), Position.DUMMY) + val astConstant1 = PtConstant("c1", DataType.UWORD, 12345.0, Position.DUMMY) + val astConstant2 = PtConstant("blockc", DataType.UWORD, 999.0, Position.DUMMY) + astBlock1.add(astConstant1) + astBlock1.add(astConstant2) + val astSub1 = PtSub("sub1", emptyList(), null, Position.DUMMY) + val astSub2 = PtSub("sub2", emptyList(), null, Position.DUMMY) + val astSub1v1 = PtVariable("v1", DataType.BYTE, null, null, Position.DUMMY) + val astSub1v2 = PtVariable("v2", DataType.BYTE, null, null, Position.DUMMY) + val astSub2v1 = PtVariable("v1", DataType.BYTE, null, null, Position.DUMMY) + val astSub2v2 = PtVariable("v2", DataType.BYTE, null, null, Position.DUMMY) + astSub1.add(astSub1v1) + astSub1.add(astSub1v2) + astSub2.add(astSub2v2) + astSub2.add(astSub2v2) + astBlock1.add(astSub1) + astBlock1.add(astSub2) + val astBfunc = PtIdentifier("msb", DataType.UBYTE, Position.DUMMY) + astBlock1.add(astBfunc) + val astBlock2 = PtBlock("block2", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("block2"), Position.DUMMY) + val astSub21 = PtSub("sub1", emptyList(), null, Position.DUMMY) + val astSub22 = PtSub("sub2", emptyList(), null, Position.DUMMY) + val astSub221 = PtSub("subsub", emptyList(), null, Position.DUMMY) + val astLabel = PtLabel("label", Position.DUMMY) + astSub221.add(astLabel) + astSub22.add(astSub221) + astBlock2.add(astSub21) + astBlock2.add(astSub22) + astProgram.add(astBlock1) + astProgram.add(astBlock2) + + // now hook up the SymbolTable on that AST + val st = SymbolTable(astProgram) + val block1 = StNode("block1", StNodeType.BLOCK, astBlock1) + val sub11 = StNode("sub1", StNodeType.SUBROUTINE, astSub1) + val sub12 = StNode("sub2", StNodeType.SUBROUTINE, astSub2) block1.add(sub11) block1.add(sub12) - block1.add(StConstant("c1", DataType.UWORD, 12345.0, Position.DUMMY)) - block1.add(StConstant("blockc", DataType.UWORD, 999.0, Position.DUMMY)) - sub11.add(StStaticVariable("v1", DataType.BYTE, true, null, null, null, null, ZeropageWish.DONTCARE, Position.DUMMY)) - sub11.add(StStaticVariable("v2", DataType.BYTE, true, null, null, null, null, ZeropageWish.DONTCARE, Position.DUMMY)) - sub12.add(StStaticVariable("v1", DataType.BYTE, true, null, null, null, null, ZeropageWish.DONTCARE, Position.DUMMY)) - sub12.add(StStaticVariable("v2", DataType.BYTE, true, null, null, null, null, ZeropageWish.DONTCARE, Position.DUMMY)) + block1.add(StConstant("c1", DataType.UWORD, 12345.0, astConstant1)) + block1.add(StConstant("blockc", DataType.UWORD, 999.0, astConstant2)) + sub11.add(StStaticVariable("v1", DataType.BYTE, true, null, null, null, null, ZeropageWish.DONTCARE, astSub1v1)) + sub11.add(StStaticVariable("v2", DataType.BYTE, true, null, null, null, null, ZeropageWish.DONTCARE, astSub1v2)) + sub12.add(StStaticVariable("v1", DataType.BYTE, true, null, null, null, null, ZeropageWish.DONTCARE, astSub2v1)) + sub12.add(StStaticVariable("v2", DataType.BYTE, true, null, null, null, null, ZeropageWish.DONTCARE, astSub2v2)) - val block2 = StNode("block2", StNodeType.BLOCK, Position.DUMMY) - val sub21 = StNode("sub1", StNodeType.SUBROUTINE, Position.DUMMY) - val sub22 = StNode("sub2", StNodeType.SUBROUTINE, Position.DUMMY) + val block2 = StNode("block2", StNodeType.BLOCK, astBlock2) + val sub21 = StNode("sub1", StNodeType.SUBROUTINE, astSub21) + val sub22 = StNode("sub2", StNodeType.SUBROUTINE, astSub22) block2.add(sub21) block2.add(sub22) - val sub221 = StNode("subsub", StNodeType.SUBROUTINE, Position.DUMMY) - sub221.add(StNode("label", StNodeType.LABEL, Position.DUMMY)) + val sub221 = StNode("subsub", StNodeType.SUBROUTINE, astSub221) + sub221.add(StNode("label", StNodeType.LABEL, astLabel)) sub22.add(sub221) - val builtinfunc = StNode("msb", StNodeType.BUILTINFUNC, Position.DUMMY) + val builtinfunc = StNode("msb", StNodeType.BUILTINFUNC, astBfunc) st.add(block1) st.add(block2) st.add(builtinfunc) return st -}*/ +} diff --git a/compiler/test/codegeneration/TestVarious.kt b/compiler/test/codegeneration/TestVarious.kt index 96210f04b..cfa3c52a8 100644 --- a/compiler/test/codegeneration/TestVarious.kt +++ b/compiler/test/codegeneration/TestVarious.kt @@ -1,7 +1,15 @@ package prog8tests.codegeneration import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.ints.shouldBeGreaterThan +import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe +import io.kotest.matchers.string.shouldStartWith +import io.kotest.matchers.types.instanceOf +import prog8.code.ast.PtArrayIndexer +import prog8.code.ast.PtAssignment +import prog8.code.ast.PtVariable +import prog8.code.core.DataType import prog8.code.target.C64Target import prog8tests.helpers.compileText @@ -58,4 +66,29 @@ main { }""" compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null } + + + test("ast result from compileText") { + val text=""" +main { + sub start() { + uword[3] seed + cx16.r0 = seed[0] + seed[1] + seed[2] + } +}""" + val result = compileText(C64Target(), false, text, writeAssembly = true)!! + result.compilerAst.name shouldStartWith "on_the_fly" + result.codegenAst!!.name shouldBe result.compilerAst.name + result.codegenAst!!.children.size shouldBeGreaterThan 2 + val start = result.codegenAst!!.entrypoint()!! + start.name shouldBe "start" + start.children.size shouldBeGreaterThan 2 + val seed = start.children[0] as PtVariable + seed.name shouldBe "seed" + seed.value shouldBe null + seed.type shouldBe DataType.ARRAY_UW + val assign = start.children[1] as PtAssignment + assign.target.identifier!!.name shouldBe "cx16.r0" + assign.value shouldBe instanceOf() + } }) \ No newline at end of file diff --git a/intermediate/src/prog8/intermediate/IRFileReader.kt b/intermediate/src/prog8/intermediate/IRFileReader.kt index 043725009..590393910 100644 --- a/intermediate/src/prog8/intermediate/IRFileReader.kt +++ b/intermediate/src/prog8/intermediate/IRFileReader.kt @@ -172,7 +172,7 @@ class IRFileReader { val dt: DataType = parseDatatype(type, arraysize!=null) val zp = if(zpwish.isBlank()) ZeropageWish.DONTCARE else ZeropageWish.valueOf(zpwish) val dummyNode = PtVariable(name, dt, null, null, Position.DUMMY) - val newVar = StStaticVariable(name, dt, true, null, null, null, arraysize, zp, dummyNode, Position.DUMMY) + val newVar = StStaticVariable(name, dt, true, null, null, null, arraysize, zp, dummyNode) bssVariables.add(newVar) } return bssVariables @@ -235,7 +235,7 @@ class IRFileReader { } require(!bss) { "bss var should be in BSS section" } val dummyNode = PtVariable(name, dt, null, null, Position.DUMMY) - variables.add(StStaticVariable(name, dt, bss, initNumeric, null, initArray, arraysize, zp, dummyNode, Position.DUMMY)) + variables.add(StStaticVariable(name, dt, bss, initNumeric, null, initArray, arraysize, zp, dummyNode)) } return variables } @@ -262,7 +262,7 @@ class IRFileReader { val arraysize = if(arrayspec.isNotBlank()) arrayspec.substring(1, arrayspec.length-1).toInt() else null val dt: DataType = parseDatatype(type, arraysize!=null) val dummyNode = PtVariable(name, dt, null, null, Position.DUMMY) - memvars.add(StMemVar(name, dt, parseIRValue(address).toUInt(), arraysize, dummyNode, Position.DUMMY)) + memvars.add(StMemVar(name, dt, parseIRValue(address).toUInt(), arraysize, dummyNode)) } memvars } @@ -285,7 +285,7 @@ class IRFileReader { val match = slabPattern.matchEntire(line) ?: throw IRParseException("invalid SLAB $line") val (name, size, align) = match.destructured val dummyNode = PtVariable(name, DataType.ARRAY_UB, null, null, Position.DUMMY) - slabs.add(StMemorySlab(name, size.toUInt(), align.toUInt(), dummyNode, Position.DUMMY)) + slabs.add(StMemorySlab(name, size.toUInt(), align.toUInt(), dummyNode)) } slabs } diff --git a/intermediate/src/prog8/intermediate/IRSymbolTable.kt b/intermediate/src/prog8/intermediate/IRSymbolTable.kt index f4a929319..e9251b0ed 100644 --- a/intermediate/src/prog8/intermediate/IRSymbolTable.kt +++ b/intermediate/src/prog8/intermediate/IRSymbolTable.kt @@ -73,15 +73,14 @@ class IRSymbolTable(sourceSt: SymbolTable?) { return newArray } scopedName = variable.scopedName - val dummyNode = PtVariable(scopedName, variable.dt, null, null, variable.position) + val dummyNode = PtVariable(scopedName, variable.dt, null, null, variable.astNode.position) varToadd = StStaticVariable(scopedName, variable.dt, variable.bss, variable.onetimeInitializationNumericValue, variable.onetimeInitializationStringValue, fixupAddressOfInArray(variable.onetimeInitializationArrayValue), variable.length, variable.zpwish, - dummyNode, - variable.position + dummyNode ) } table[scopedName] = varToadd @@ -96,8 +95,8 @@ class IRSymbolTable(sourceSt: SymbolTable?) { varToadd = variable } else { scopedName = variable.scopedName - val dummyNode = PtVariable(scopedName, variable.dt, null, null, variable.position) - varToadd = StMemVar(scopedName, variable.dt, variable.address, variable.length, dummyNode, variable.position) + val dummyNode = PtVariable(scopedName, variable.dt, null, null, variable.astNode.position) + varToadd = StMemVar(scopedName, variable.dt, variable.address, variable.length, dummyNode) } table[scopedName] = varToadd } @@ -106,8 +105,8 @@ class IRSymbolTable(sourceSt: SymbolTable?) { val varToadd = if('.' in variable.name) variable else { - val dummyNode = PtVariable(variable.name, DataType.ARRAY_UB, null, null, variable.position) - StMemorySlab("prog8_slabs.${variable.name}", variable.size, variable.align, dummyNode, variable.position) + val dummyNode = PtVariable(variable.name, DataType.ARRAY_UB, null, null, variable.astNode.position) + StMemorySlab("prog8_slabs.${variable.name}", variable.size, variable.align, dummyNode) } table[varToadd.name] = varToadd }