mirror of
https://github.com/irmen/prog8.git
synced 2025-02-16 22:30:46 +00:00
redo 8e730ef93d0f6a3cc1583e05f8f0ace8b8c07515 to avoid larger code generated
This commit is contained in:
parent
3feb3e52f8
commit
c25eb088ec
@ -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<List<String>, StNode> by lazy {
|
||||
val result = mutableMapOf<List<String>, StNode>()
|
||||
val flat: Map<String, StNode> by lazy {
|
||||
val result = mutableMapOf<String, StNode>()
|
||||
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<String>, default: () -> StNode) =
|
||||
lookup(scopedName) ?: default()
|
||||
fun lookupOrElse(scopedName: String, default: () -> StNode): StNode =
|
||||
lookup(scopedName.split('.')) ?: default()
|
||||
|
||||
private val scopedNameList: List<String> by lazy {
|
||||
if(type== StNodeType.GLOBAL)
|
||||
emptyList()
|
||||
else
|
||||
parent.scopedNameList + name
|
||||
}
|
||||
|
||||
private fun lookup(scopedName: List<String>): 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<String> by lazy {
|
||||
if(type== StNodeType.GLOBAL)
|
||||
emptyList()
|
||||
else
|
||||
parent.scopedNameList + name
|
||||
}
|
||||
|
||||
private fun lookup(scopedName: List<String>): 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<String, Encoding>
|
||||
typealias StArray = List<StArrayElement>
|
||||
|
@ -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<String>,
|
||||
val name: String,
|
||||
val alloc: MemoryAllocator.VarAllocation,
|
||||
val value: Pair<String, Encoding>
|
||||
)
|
||||
|
||||
private class ZpArrayWithInitial(
|
||||
val name: List<String>,
|
||||
val name: String,
|
||||
val alloc: MemoryAllocator.VarAllocation,
|
||||
val value: StArray
|
||||
)
|
||||
@ -443,9 +443,9 @@ internal class ProgramAndVarsGen(
|
||||
val result = mutableListOf<ZpStringWithInitial>()
|
||||
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<ZpArrayWithInitial>()
|
||||
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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -88,7 +88,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
private fun translate(check: PtContainmentCheck, resultRegister: Int, resultFpRegister: Int): IRCodeChunks {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
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<IRCodeChunkBase>()
|
||||
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
|
||||
|
@ -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<PtSubroutineParameter>) =
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -3,7 +3,7 @@ TODO
|
||||
|
||||
For next release
|
||||
^^^^^^^^^^^^^^^^
|
||||
- optimize scoped symbols: .split('.') / .joinToString(".") / 'dotted string' comments
|
||||
- optimize scoped symbols: .split('.') / .joinToString(".")
|
||||
|
||||
...
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user