diff --git a/codeCore/src/prog8/code/SymbolTable.kt b/codeCore/src/prog8/code/SymbolTable.kt index 2556e5cd7..fca4c2675 100644 --- a/codeCore/src/prog8/code/SymbolTable.kt +++ b/codeCore/src/prog8/code/SymbolTable.kt @@ -13,12 +13,10 @@ class SymbolTable : StNode("", StNodeType.GLOBAL, Position.DUMMY) { * This gives the fastest lookup possible (no need to traverse tree nodes) */ - // TODO key as dotted string instead of list - - val flat: Map, StNode> by lazy { - val result = mutableMapOf, StNode>() + val flat: Map by lazy { + val result = mutableMapOf() fun flatten(node: StNode) { - result[node.scopedName.split('.')] = node + result[node.scopedName] = node node.children.values.forEach { flatten(it) } } children.values.forEach { flatten(it) } @@ -57,7 +55,7 @@ class SymbolTable : StNode("", StNodeType.GLOBAL, Position.DUMMY) { children.mapNotNull { if (it.value.type == StNodeType.MEMORYSLAB) it.value as StMemorySlab else null } } - override fun lookup(scopedName: String) = flat[scopedName.split('.')] // TODO dotted string as keys + override fun lookup(scopedName: String) = flat[scopedName] } @@ -84,42 +82,18 @@ open class StNode(val name: String, lateinit var parent: StNode - val scopedName: String by lazy { - scopedNameList.joinToString(".") - } + val scopedName: String by lazy { scopedNameList.joinToString(".") } open fun lookup(scopedName: String) = lookup(scopedName.split('.')) - fun lookupUnqualifiedOrElse(name: String, default: () -> StNode) = - lookupUnqualified(name) ?: default() + fun lookupUnscopedOrElse(name: String, default: () -> StNode) = + lookupUnscoped(name) ?: default() - fun lookupQualifiedOrElse(scopedName: List, default: () -> StNode) = - lookup(scopedName) ?: default() + fun lookupOrElse(scopedName: String, default: () -> StNode): StNode = + lookup(scopedName.split('.')) ?: default() - private val scopedNameList: List by lazy { - if(type== StNodeType.GLOBAL) - emptyList() - else - parent.scopedNameList + name - } - - private fun lookup(scopedName: List): StNode? { - // a scoped name refers to a name in another namespace, and always stars from the root. - var node = this - while(node.type!= StNodeType.GLOBAL) - node = node.parent - - for(name in scopedName) { - if(name in node.children) - node = node.children.getValue(name) - else - return null - } - return node - } - - fun lookupUnqualified(name: String): StNode? { + fun lookupUnscoped(name: String): StNode? { // first consider the builtin functions var globalscope = this while(globalscope.type!= StNodeType.GLOBAL) @@ -145,6 +119,28 @@ open class StNode(val name: String, children[child.name] = child child.parent = this } + + private val scopedNameList: List by lazy { + if(type== StNodeType.GLOBAL) + emptyList() + else + parent.scopedNameList + name + } + + private fun lookup(scopedName: List): StNode? { + // a scoped name refers to a name in another namespace, and always stars from the root. + var node = this + while(node.type!= StNodeType.GLOBAL) + node = node.parent + + for(name in scopedName) { + if(name in node.children) + node = node.children.getValue(name) + else + return null + } + return node + } } class StStaticVariable(name: String, @@ -222,11 +218,7 @@ class StRomSub(name: String, class StSubroutineParameter(val name: String, val type: DataType) class StRomSubParameter(val register: RegisterOrStatusflag, val type: DataType) -class StArrayElement(val number: Double?, val addressOfSymbol: String?) { - init { - require(addressOfSymbol==null || addressOfSymbol.contains('.')) - } -} +class StArrayElement(val number: Double?, val addressOfSymbol: String?) typealias StString = Pair typealias StArray = List diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt index 257f797e5..d074da5d6 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt @@ -223,7 +223,7 @@ internal class ProgramAndVarsGen( scope.children.filter { it.value.type in arrayOf(StNodeType.STATICVAR, StNodeType.CONSTANT, StNodeType.MEMVAR) } private fun createBlockVariables(block: Block) { - val scope = symboltable.lookupUnqualifiedOrElse(block.name) { throw AssemblyError("lookup") } + val scope = symboltable.lookupUnscopedOrElse(block.name) { throw AssemblyError("lookup") } require(scope.type==StNodeType.BLOCK) val varsInBlock = getVars(scope) @@ -286,7 +286,7 @@ internal class ProgramAndVarsGen( // regular subroutine asmgen.out("${sub.name}\t$asmStartScope") - val scope = symboltable.lookupQualifiedOrElse(sub.scopedName) { throw AssemblyError("lookup") } + val scope = symboltable.lookupOrElse(sub.scopedName.joinToString(".")) { throw AssemblyError("lookup") } require(scope.type==StNodeType.SUBROUTINE) val varsInSubroutine = getVars(scope) @@ -428,13 +428,13 @@ internal class ProgramAndVarsGen( } private class ZpStringWithInitial( - val name: List, + val name: String, val alloc: MemoryAllocator.VarAllocation, val value: Pair ) private class ZpArrayWithInitial( - val name: List, + val name: String, val alloc: MemoryAllocator.VarAllocation, val value: StArray ) @@ -443,9 +443,9 @@ internal class ProgramAndVarsGen( val result = mutableListOf() val vars = allocator.zeropageVars.filter { it.value.dt==DataType.STR } for (variable in vars) { - val svar = symboltable.flat.getValue(variable.key.split('.')) as StStaticVariable + val svar = symboltable.flat.getValue(variable.key) as StStaticVariable if(svar.onetimeInitializationStringValue!=null) - result.add(ZpStringWithInitial(variable.key.split('.'), variable.value, svar.onetimeInitializationStringValue!!)) + result.add(ZpStringWithInitial(variable.key, variable.value, svar.onetimeInitializationStringValue!!)) } return result } @@ -454,9 +454,9 @@ internal class ProgramAndVarsGen( val result = mutableListOf() val vars = allocator.zeropageVars.filter { it.value.dt in ArrayDatatypes } for (variable in vars) { - val svar = symboltable.flat.getValue(variable.key.split('.')) as StStaticVariable + val svar = symboltable.flat.getValue(variable.key) as StStaticVariable if(svar.onetimeInitializationArrayValue!=null) - result.add(ZpArrayWithInitial(variable.key.split('.'), variable.value, svar.onetimeInitializationArrayValue!!)) + result.add(ZpArrayWithInitial(variable.key, variable.value, svar.onetimeInitializationArrayValue!!)) } return result } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt index 2a9109ad8..f6ba9f801 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/VariableAllocator.kt @@ -88,7 +88,7 @@ internal class VariableAllocator(private val symboltable: SymbolTable, // try to allocate any other interger variables into the zeropage until it is full. // TODO some form of intelligent priorization? most often used variables first? loopcounter vars first? ...? if(errors.noErrors()) { - for (variable in varsDontCare.sortedBy { it.scopedName.length }) { + for (variable in varsDontCare.sortedBy { it.scopedName.count { chr -> chr=='.'}}) { if(variable.dt in IntegerDatatypes) { if(zeropage.free.isEmpty()) { break diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt index 3699e0b7f..d5a6e11c9 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt @@ -60,7 +60,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe private fun funcAny(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks { val arrayName = call.args[0] as PtIdentifier - val array = codeGen.symbolTable.flat.getValue(arrayName.name.split('.')) as StStaticVariable + val array = codeGen.symbolTable.flat.getValue(arrayName.name) as StStaticVariable val syscall = when (array.dt) { DataType.ARRAY_UB, @@ -83,7 +83,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe private fun funcAll(call: PtBuiltinFunctionCall, resultRegister: Int): IRCodeChunks { val arrayName = call.args[0] as PtIdentifier - val array = codeGen.symbolTable.flat.getValue(arrayName.name.split('.')) as StStaticVariable + val array = codeGen.symbolTable.flat.getValue(arrayName.name) as StStaticVariable val syscall = when(array.dt) { DataType.ARRAY_UB, @@ -204,7 +204,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe private fun funcReverse(call: PtBuiltinFunctionCall): IRCodeChunks { val arrayName = call.args[0] as PtIdentifier - val array = codeGen.symbolTable.flat.getValue(arrayName.name.split('.')) as StStaticVariable + val array = codeGen.symbolTable.flat.getValue(arrayName.name) as StStaticVariable val syscall = when(array.dt) { DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> IMSyscall.REVERSE_BYTES @@ -223,7 +223,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe private fun funcSort(call: PtBuiltinFunctionCall): IRCodeChunks { val arrayName = call.args[0] as PtIdentifier - val array = codeGen.symbolTable.flat.getValue(arrayName.name.split('.')) as StStaticVariable + val array = codeGen.symbolTable.flat.getValue(arrayName.name) as StStaticVariable val syscall = when(array.dt) { DataType.ARRAY_UB -> IMSyscall.SORT_UBYTE diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt index 6d3a19ebe..a93a39b5e 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt @@ -88,7 +88,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { private fun translate(check: PtContainmentCheck, resultRegister: Int, resultFpRegister: Int): IRCodeChunks { val result = mutableListOf() result += translateExpression(check.element, resultRegister, -1) // load the element to check in resultRegister - val iterable = codeGen.symbolTable.flat.getValue(check.iterable.name.split('.')) as StStaticVariable + val iterable = codeGen.symbolTable.flat.getValue(check.iterable.name) as StStaticVariable when(iterable.dt) { DataType.STR -> { result += translateExpression(check.element, SyscallRegisterBase, -1) @@ -931,7 +931,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { } fun translate(fcall: PtFunctionCall, resultRegister: Int, resultFpRegister: Int): IRCodeChunks { - when (val callTarget = codeGen.symbolTable.flat.getValue(fcall.name.split('.'))) { + when (val callTarget = codeGen.symbolTable.flat.getValue(fcall.name)) { is StSub -> { val result = mutableListOf() for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) { diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index 711701203..d397d5399 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -131,7 +131,7 @@ class IRCodeGen( symbol = symbolExpr index = 0u } - val target = symbolTable.flat[symbol.split('.')] + val target = symbolTable.flat[symbol] if (target is StMemVar) { replacements.add(Triple(chunk, idx, target.address+index)) } @@ -1248,7 +1248,7 @@ class IRCodeGen( private fun translate(parameters: List) = parameters.map { val flattenedName = it.definingSub()!!.scopedName + "." + it.name - val orig = symbolTable.flat.getValue(flattenedName.split('.')) as StStaticVariable + val orig = symbolTable.flat.getValue(flattenedName) as StStaticVariable IRSubroutine.IRParam(flattenedName, orig.dt) } diff --git a/compiler/test/TestSymbolTable.kt b/compiler/test/TestSymbolTable.kt index 3aa693b5a..8ac9f0385 100644 --- a/compiler/test/TestSymbolTable.kt +++ b/compiler/test/TestSymbolTable.kt @@ -21,50 +21,51 @@ class TestSymbolTable: FunSpec({ test("symboltable flatten") { val st = makeSt() - st.flat[listOf("zzzzz")] shouldBe null - st.flat.getValue(listOf("msb")).type shouldBe StNodeType.BUILTINFUNC - st.flat.getValue(listOf("block2")).type shouldBe StNodeType.BLOCK - st.flat.getValue(listOf("block2", "sub2", "subsub", "label")).type shouldBe StNodeType.LABEL - st.flat[listOf("block2", "sub2", "subsub", "label", "zzzz")] shouldBe null + st.flat["zzzzz"] shouldBe null + st.flat.getValue("msb").type shouldBe StNodeType.BUILTINFUNC + st.flat.getValue("block2").type shouldBe StNodeType.BLOCK + st.flat.getValue("block2.sub2.subsub.label").type shouldBe StNodeType.LABEL + st.flat["block2.sub2.subsub.label.zzzz"] shouldBe null } test("symboltable global lookups") { val st = makeSt() - st.lookupUnqualified("undefined") shouldBe null + st.lookupUnscoped("undefined") shouldBe null st.lookup("undefined") shouldBe null - var default = st.lookupUnqualifiedOrElse("undefined") { StNode("default", StNodeType.LABEL, Position.DUMMY) } + st.lookup("undefined.undefined") shouldBe null + var default = st.lookupUnscopedOrElse("undefined") { StNode("default", StNodeType.LABEL, Position.DUMMY) } default.name shouldBe "default" - default = st.lookupUnqualifiedOrElse("undefined") { StNode("default", StNodeType.LABEL, Position.DUMMY) } + default = st.lookupUnscopedOrElse("undefined") { StNode("default", StNodeType.LABEL, Position.DUMMY) } default.name shouldBe "default" - val msbFunc = st.lookupUnqualifiedOrElse("msb") { fail("msb must be found") } + val msbFunc = st.lookupUnscopedOrElse("msb") { fail("msb must be found") } msbFunc.type shouldBe StNodeType.BUILTINFUNC - val variable = st.lookupQualifiedOrElse(listOf("block1", "sub2", "v2")) { fail("v2 must be found") } + val variable = st.lookupOrElse("block1.sub2.v2") { fail("v2 must be found") } variable.type shouldBe StNodeType.STATICVAR } test("symboltable nested lookups") { val st = makeSt() - val sub1 = st.lookupQualifiedOrElse(listOf("block1", "sub1")) { fail("should find sub1") } + val sub1 = st.lookupOrElse("block1.sub1") { fail("should find sub1") } sub1.name shouldBe "sub1" sub1.scopedName shouldBe "block1.sub1" sub1.type shouldBe StNodeType.SUBROUTINE sub1.children.size shouldBe 2 - val v1 = sub1.lookupUnqualifiedOrElse("v1") { fail("v1 must be found") } as StStaticVariable + val v1 = sub1.lookupUnscopedOrElse("v1") { fail("v1 must be found") } as StStaticVariable v1.type shouldBe StNodeType.STATICVAR v1.name shouldBe "v1" v1.dt shouldBe DataType.BYTE - val blockc = sub1.lookupUnqualifiedOrElse("blockc") { fail("blockc") } as StConstant + val blockc = sub1.lookupUnscopedOrElse("blockc") { fail("blockc") } as StConstant blockc.type shouldBe StNodeType.CONSTANT blockc.value shouldBe 999.0 - val subsub = st.lookupQualifiedOrElse(listOf("block2", "sub2", "subsub")) { fail("should find subsub") } - subsub.lookupUnqualified("blockc") shouldBe null - subsub.lookupUnqualified("label") shouldNotBe null + val subsub = st.lookupOrElse("block2.sub2.subsub") { fail("should find subsub") } + subsub.lookupUnscoped("blockc") shouldBe null + subsub.lookupUnscoped("label") shouldNotBe null } }) diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 3f184e01d..c72f70645 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,7 +3,7 @@ TODO For next release ^^^^^^^^^^^^^^^^ -- optimize scoped symbols: .split('.') / .joinToString(".") / 'dotted string' comments +- optimize scoped symbols: .split('.') / .joinToString(".") ...