tweak SymbolTable and fix its unittest

This commit is contained in:
Irmen de Jong 2023-02-09 22:49:34 +01:00
parent fe29d8a23f
commit 30d20a453b
7 changed files with 146 additions and 68 deletions

View File

@ -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<String, StNode> = 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<StSubroutineParameter>, val returnType: DataType?, astNode: PtNode, position: Position) :
StNode(name, StNodeType.SUBROUTINE, position, astNode) {
class StSub(name: String, val parameters: List<StSubroutineParameter>, val returnType: DataType?, astNode: PtNode) :
StNode(name, StNodeType.SUBROUTINE, astNode) {
}
@ -216,9 +213,8 @@ class StRomSub(name: String,
val address: UInt,
val parameters: List<StRomSubParameter>,
val returns: List<StRomSubParameter>,
astNode: PtNode,
position: Position) :
StNode(name, StNodeType.ROMSUB, position, astNode)
astNode: PtNode) :
StNode(name, StNodeType.ROMSUB, astNode)

View File

@ -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<StNode>()
@ -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
}

View File

@ -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++ }

View File

@ -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
}*/
}

View File

@ -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<PtArrayIndexer>()
}
})

View File

@ -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
}

View File

@ -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
}