From 496245c801e666c2d433416789dee45b647e2d86 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Fri, 4 Mar 2022 23:25:26 +0100 Subject: [PATCH] working on symboltable --- .../src/prog8/codegen/cpu6502/AsmGen.kt | 3 +- .../codegen/cpu6502/ProgramAndVarsGen.kt | 3 + .../prog8/codegen/experimental6502/AsmGen.kt | 4 +- .../codegen/experimental6502/SymbolTable.kt | 82 ---------- compiler/src/prog8/compiler/Compiler.kt | 15 +- .../astprocessing}/SymbolTableMaker.kt | 30 ++-- .../astprocessing/VariablesAndConsts.kt | 2 +- compiler/test/TestOptimization.kt | 3 +- compiler/test/TestSymbolTable.kt | 93 ++++++++++++ .../test/codegeneration/TestAsmGenSymbols.kt | 49 ++---- compiler/test/helpers/compileXyz.kt | 7 +- .../compilerinterface/IVariablesAndConsts.kt | 3 + .../prog8/compilerinterface/SymbolTable.kt | 143 ++++++++++++++++++ 13 files changed, 294 insertions(+), 143 deletions(-) delete mode 100644 codeGenExperimental6502/src/prog8/codegen/experimental6502/SymbolTable.kt rename {codeGenExperimental6502/src/prog8/codegen/experimental6502 => compiler/src/prog8/compiler/astprocessing}/SymbolTableMaker.kt (71%) create mode 100644 compiler/test/TestSymbolTable.kt create mode 100644 compilerInterfaces/src/prog8/compilerinterface/SymbolTable.kt diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index 0c4dec531..7c779ea76 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -23,6 +23,7 @@ internal const val subroutineFloatEvalResultVar2 = "prog8_float_eval_result2" class AsmGen(internal val program: Program, internal val errors: IErrorReporter, + internal val symbolTable: SymbolTable, internal val variables: IVariablesAndConsts, internal val options: CompilationOptions): IAssemblyGenerator { @@ -37,7 +38,7 @@ class AsmGen(internal val program: Program, private val postincrdecrAsmGen = PostIncrDecrAsmGen(program, this) private val functioncallAsmGen = FunctionCallAsmGen(program, this) private val expressionsAsmGen = ExpressionsAsmGen(program, this, allocator) - private val programGen = ProgramAndVarsGen(program, variables, options, errors, functioncallAsmGen, this, allocator, zeropage) + private val programGen = ProgramAndVarsGen(program, variables, symbolTable, options, errors, functioncallAsmGen, this, allocator, zeropage) private val assignmentAsmGen = AssignmentAsmGen(program, this, allocator) private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen, allocator) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt index c18e68b76..307db2614 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt @@ -24,6 +24,7 @@ import kotlin.math.absoluteValue internal class ProgramAndVarsGen( val program: Program, val variables: IVariablesAndConsts, + val symboltable: SymbolTable, val options: CompilationOptions, val errors: IErrorReporter, private val functioncallAsmGen: FunctionCallAsmGen, @@ -383,6 +384,8 @@ internal class ProgramAndVarsGen( } private fun zeropagevars2asm(block: Block) { + //val scope = symboltable.lookupOrElse(block.name) { throw AssemblyError("lookup fail") } + //require(scope.type==StNodeType.BLOCK) val varnames = variables.blockVars.getOrDefault(block, emptySet()).map { it.scopedname }.toSet() zeropagevars2asm(varnames) } diff --git a/codeGenExperimental6502/src/prog8/codegen/experimental6502/AsmGen.kt b/codeGenExperimental6502/src/prog8/codegen/experimental6502/AsmGen.kt index 92731387a..eed16fa89 100644 --- a/codeGenExperimental6502/src/prog8/codegen/experimental6502/AsmGen.kt +++ b/codeGenExperimental6502/src/prog8/codegen/experimental6502/AsmGen.kt @@ -5,6 +5,7 @@ import prog8.compilerinterface.* class AsmGen(internal val program: Program, internal val errors: IErrorReporter, + internal val symbolTable: SymbolTable, internal val variables: IVariablesAndConsts, internal val options: CompilationOptions): IAssemblyGenerator { @@ -12,10 +13,9 @@ class AsmGen(internal val program: Program, println("\n** experimental 65(c)02 code generator **\n") - val stMaker = SymbolTableMaker() - val symbolTable = stMaker.make(program) symbolTable.print() + println("..todo: create assembly code into ${options.outputDir.toAbsolutePath()}..") return AssemblyProgram("dummy") } diff --git a/codeGenExperimental6502/src/prog8/codegen/experimental6502/SymbolTable.kt b/codeGenExperimental6502/src/prog8/codegen/experimental6502/SymbolTable.kt deleted file mode 100644 index 2d27b9c12..000000000 --- a/codeGenExperimental6502/src/prog8/codegen/experimental6502/SymbolTable.kt +++ /dev/null @@ -1,82 +0,0 @@ -package prog8.codegen.experimental6502 - -import prog8.ast.base.DataType -import prog8.ast.base.Position -import prog8.ast.toHex - - -enum class StNodeType { - GLOBAL, - // MODULE, // not used with current scoping rules - BLOCK, - SUBROUTINE, - LABEL, - VARIABLE, - MEMVAR, - CONSTANT, - BUILTINFUNC -} - -open class StNode(val name: String, - val type: StNodeType, - val position: Position, - val children: MutableMap = mutableMapOf() -) { - - lateinit var parent: StNode - - val scopedName: List by lazy { - if(type==StNodeType.GLOBAL) - emptyList() - else - parent.scopedName + name - } - - fun printIndented(indent: Int) { - print(" ".repeat(indent)) - when(type) { - StNodeType.GLOBAL -> print("SYMBOL-TABLE:") - StNodeType.BLOCK -> print("[B] ") - StNodeType.SUBROUTINE -> print("[S] ") - StNodeType.LABEL -> print("[L] ") - StNodeType.VARIABLE -> print("[V] ") - StNodeType.MEMVAR -> print("[M] ") - StNodeType.CONSTANT -> print("[C] ") - StNodeType.BUILTINFUNC -> print("[F] ") - } - printProperties() - println(" pos=$position sn=$scopedName") - children.forEach { (_, node) -> node.printIndented(indent+1) } - } - - open fun printProperties() { - print("$name ") - } -} - -class SymbolTable() : StNode("", StNodeType.GLOBAL, Position.DUMMY) { - fun print() = printIndented(0) - - override fun printProperties() { } -} - -class StVariable(name: String, val dt: DataType, position: Position) : StNode(name, StNodeType.VARIABLE, position) { - override fun printProperties() { - print("dt=$dt") - } -} - -class StConstant(name: String, val dt: DataType, val value: Double, position: Position) : - StNode(name, StNodeType.CONSTANT, position) { - override fun printProperties() { - print("dt=$dt value=$value") - } -} - -class StMemVar(name: String, val dt: DataType, val address: UInt, position: Position) : - StNode(name, StNodeType.MEMVAR, position -) { - override fun printProperties() { - print("dt=$dt address=${address.toHex()}") - } -} diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index e212c6243..fdee58e4c 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -363,7 +363,8 @@ private fun writeAssembly(program: Program, compilerOptions.compTarget.machine.initializeZeropage(compilerOptions) program.processAstBeforeAsmGeneration(compilerOptions, errors) errors.report() - val variables = VariableExtractor().extractVars(program) + val variables = VariableExtractor().extractFrom(program) + val symbolTable = SymbolTableMaker().makeFrom(program) // TODO make removing all VarDecls work, but this needs inferType to be able to get its information from somewhere else as the VarDecl nodes in the Ast, // or don't use inferType at all anymore and "bake the type information" into the Ast somehow. @@ -374,7 +375,7 @@ private fun writeAssembly(program: Program, // println("*********** AST RIGHT BEFORE ASM GENERATION *************") // printProgram(program) - val assembly = asmGeneratorFor(program, errors, variables, compilerOptions).compileToAssembly() + val assembly = asmGeneratorFor(program, errors, symbolTable, variables, compilerOptions).compileToAssembly() errors.report() return if(assembly!=null && errors.noErrors()) { @@ -414,14 +415,18 @@ fun printProgram(program: Program) { println() } -internal fun asmGeneratorFor(program: Program, errors: IErrorReporter, variables: IVariablesAndConsts, options: CompilationOptions): IAssemblyGenerator +internal fun asmGeneratorFor(program: Program, + errors: IErrorReporter, + symbolTable: SymbolTable, + variables: IVariablesAndConsts, + options: CompilationOptions): IAssemblyGenerator { if(options.experimentalCodegen) { if (options.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02)) - return prog8.codegen.experimental6502.AsmGen(program, errors, variables, options) + return prog8.codegen.experimental6502.AsmGen(program, errors, symbolTable, variables, options) } else { if (options.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02)) - return prog8.codegen.cpu6502.AsmGen(program, errors, variables, options) + return prog8.codegen.cpu6502.AsmGen(program, errors, symbolTable, variables, options) } throw NotImplementedError("no asm generator for cpu ${options.compTarget.machine.cpu}") diff --git a/codeGenExperimental6502/src/prog8/codegen/experimental6502/SymbolTableMaker.kt b/compiler/src/prog8/compiler/astprocessing/SymbolTableMaker.kt similarity index 71% rename from codeGenExperimental6502/src/prog8/codegen/experimental6502/SymbolTableMaker.kt rename to compiler/src/prog8/compiler/astprocessing/SymbolTableMaker.kt index c137751f5..58c6a6b92 100644 --- a/codeGenExperimental6502/src/prog8/codegen/experimental6502/SymbolTableMaker.kt +++ b/compiler/src/prog8/compiler/astprocessing/SymbolTableMaker.kt @@ -1,4 +1,4 @@ -package prog8.codegen.experimental6502 +package prog8.compiler.astprocessing import prog8.ast.Program import prog8.ast.base.Position @@ -9,57 +9,57 @@ import prog8.ast.statements.Label import prog8.ast.statements.Subroutine import prog8.ast.statements.VarDecl import prog8.ast.walk.IAstVisitor +import prog8.compilerinterface.* import java.util.* -class SymbolTableMaker: IAstVisitor { +internal class SymbolTableMaker: IAstVisitor { private val st = SymbolTable() private val scopestack = Stack() - fun make(program: Program): SymbolTable { + fun makeFrom(program: Program): SymbolTable { scopestack.clear() st.children.clear() this.visit(program) program.builtinFunctions.names.forEach { val node = StNode(it, StNodeType.BUILTINFUNC, Position.DUMMY) - node.parent = st - st.children[it] = node + st.add(node) } return st } override fun visit(block: Block) { val node = StNode(block.name, StNodeType.BLOCK, block.position) - node.parent = st scopestack.push(node) super.visit(block) scopestack.pop() - st.children[node.name] = node + st.add(node) + st.origAstLinks[block] = node } override fun visit(subroutine: Subroutine) { val node = StNode(subroutine.name, StNodeType.SUBROUTINE, subroutine.position) - node.parent = scopestack.peek() scopestack.push(node) super.visit(subroutine) scopestack.pop() - scopestack.peek().children[node.name] = node + scopestack.peek().add(node) + st.origAstLinks[subroutine] = node } override fun visit(decl: VarDecl) { val node = when(decl.type) { - VarDeclType.VAR -> StVariable(decl.name, decl.datatype, decl.position) + VarDeclType.VAR -> StStaticVariable(decl.name, decl.datatype, decl.value, decl.arraysize?.constIndex(), decl.zeropage, decl.position) VarDeclType.CONST -> StConstant(decl.name, decl.datatype, (decl.value as NumericLiteral).number, decl.position) VarDeclType.MEMORY -> StMemVar(decl.name, decl.datatype, (decl.value as NumericLiteral).number.toUInt(), decl.position) } - node.parent = scopestack.peek() - node.parent.children[node.name] = node + scopestack.peek().add(node) + st.origAstLinks[decl] = node } override fun visit(label: Label) { val node = StNode(label.name, StNodeType.LABEL, label.position) - node.parent = scopestack.peek() - node.parent.children[node.name] = node + scopestack.peek().add(node) + st.origAstLinks[label] = node } -} +} \ No newline at end of file diff --git a/compiler/src/prog8/compiler/astprocessing/VariablesAndConsts.kt b/compiler/src/prog8/compiler/astprocessing/VariablesAndConsts.kt index 773bd4da5..25d394257 100644 --- a/compiler/src/prog8/compiler/astprocessing/VariablesAndConsts.kt +++ b/compiler/src/prog8/compiler/astprocessing/VariablesAndConsts.kt @@ -19,7 +19,7 @@ internal class VariableExtractor: IAstVisitor { private val allSubroutineConsts = mutableMapOf>() private val allSubroutineMemoryvars = mutableMapOf>() - fun extractVars(program: Program): IVariablesAndConsts { + fun extractFrom(program: Program): IVariablesAndConsts { this.visit(program) return VariablesAndConsts( allBlockVars, allBlockConsts, allBlockMemoryvars, diff --git a/compiler/test/TestOptimization.kt b/compiler/test/TestOptimization.kt index bf6b8a166..66f0258aa 100644 --- a/compiler/test/TestOptimization.kt +++ b/compiler/test/TestOptimization.kt @@ -290,7 +290,6 @@ class TestOptimization: FunSpec({ } """ val result = compileText(C64Target(), false, src, writeAssembly = false).assertSuccess() - val variables = VariableExtractor().extractVars(result.program) // bb = (( not bb as uword) or not ww) val bbAssign = result.program.entrypoint.statements.last() as Assignment @@ -328,7 +327,7 @@ class TestOptimization: FunSpec({ ((bbAssigns1expr.right as PrefixExpression).expression as? IdentifierReference)?.nameInSource shouldBe listOf("ww") bbAssigns1expr.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE - val asm = generateAssembly(result.program, variables, options) + val asm = generateAssembly(result.program, options) asm shouldNotBe null asm!!.name.shouldNotBeBlank() } diff --git a/compiler/test/TestSymbolTable.kt b/compiler/test/TestSymbolTable.kt new file mode 100644 index 000000000..5d3ddbd2b --- /dev/null +++ b/compiler/test/TestSymbolTable.kt @@ -0,0 +1,93 @@ +package prog8tests + +import io.kotest.assertions.fail +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe +import prog8.ast.base.DataType +import prog8.ast.base.Position +import prog8.ast.statements.ZeropageWish +import prog8.compilerinterface.* + +class TestSymbolTable: FunSpec({ + test("empty symboltable") { + val st = SymbolTable() + st.scopedName shouldBe emptyList() + st.name shouldBe "" + st.type shouldBe StNodeType.GLOBAL + st.children shouldBe mutableMapOf() + st.position shouldBe Position.DUMMY + } + + test("symboltable global lookups") { + val st = makeSt() + st.print() + + st.lookup("undefined") shouldBe null + st.lookup(listOf("undefined")) shouldBe null + var default = st.lookupOrElse("undefined") { StNode("default", StNodeType.LABEL, Position.DUMMY) } + default.name shouldBe "default" + default = st.lookupOrElse(listOf("undefined")) { StNode("default", StNodeType.LABEL, Position.DUMMY) } + default.name shouldBe "default" + + val sinfunc = st.lookupOrElse("sin") { fail("sin must be found") } + sinfunc.type shouldBe StNodeType.BUILTINFUNC + + val variable = st.lookupOrElse(listOf("block1", "sub2", "v2")) { fail("v2 must be found") } + variable.type shouldBe StNodeType.STATICVAR + } + + test("symboltable nested lookups") { + val st = makeSt() + + val sub1 = st.lookupOrElse(listOf("block1", "sub1")) { fail("should find sub1") } + sub1.name shouldBe "sub1" + sub1.scopedName shouldBe listOf("block1", "sub1") + sub1.type shouldBe StNodeType.SUBROUTINE + sub1.children.size shouldBe 2 + + val v1 = sub1.lookupOrElse("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.lookupOrElse("blockc") { fail("blockc") } as StConstant + blockc.type shouldBe StNodeType.CONSTANT + blockc.value shouldBe 999.0 + + val subsub = st.lookupOrElse(listOf("block2", "sub2", "subsub")) { fail("should find subsub") } + subsub.lookup("blockc") shouldBe null + subsub.lookup("label") shouldNotBe null + } +}) + + +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) + 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, null, null, ZeropageWish.DONTCARE, Position.DUMMY)) + sub11.add(StStaticVariable("v2", DataType.BYTE, null, null, ZeropageWish.DONTCARE, Position.DUMMY)) + sub12.add(StStaticVariable("v1", DataType.BYTE, null, null, ZeropageWish.DONTCARE, Position.DUMMY)) + sub12.add(StStaticVariable("v2", DataType.BYTE, null, null, ZeropageWish.DONTCARE, Position.DUMMY)) + + val block2 = StNode("block2", StNodeType.BLOCK, Position.DUMMY) + val sub21 = StNode("sub1", StNodeType.SUBROUTINE, Position.DUMMY) + val sub22 = StNode("sub2", StNodeType.SUBROUTINE, Position.DUMMY) + block2.add(sub21) + block2.add(sub22) + val sub221 = StNode("subsub", StNodeType.SUBROUTINE, Position.DUMMY) + sub221.add(StNode("label", StNodeType.LABEL, Position.DUMMY)) + sub22.add(sub221) + + val builtinfunc = StNode("sin", StNodeType.BUILTINFUNC, Position.DUMMY) + st.add(block1) + st.add(block2) + st.add(builtinfunc) + return st +} \ No newline at end of file diff --git a/compiler/test/codegeneration/TestAsmGenSymbols.kt b/compiler/test/codegeneration/TestAsmGenSymbols.kt index 1e2c0e736..6cb2761cd 100644 --- a/compiler/test/codegeneration/TestAsmGenSymbols.kt +++ b/compiler/test/codegeneration/TestAsmGenSymbols.kt @@ -15,6 +15,8 @@ import prog8.ast.statements.* import prog8.codegen.cpu6502.AsmGen import prog8.codegen.target.C64Target import prog8.codegen.target.c64.C64Zeropage +import prog8.compiler.astprocessing.SymbolTableMaker +import prog8.compiler.astprocessing.VariableExtractor import prog8.compilerinterface.* import prog8.parser.SourceCode import prog8tests.helpers.DummyFunctions @@ -23,7 +25,7 @@ import prog8tests.helpers.DummyStringEncoder import prog8tests.helpers.ErrorReporterForTests class TestAsmGenSymbols: StringSpec({ - fun createTestProgram(): Pair { + fun createTestProgram(): Program { /* main { @@ -69,41 +71,22 @@ class TestAsmGenSymbols: StringSpec({ val module = Module(mutableListOf(block), Position.DUMMY, SourceCode.Generated("test")) val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder).addModule(module) - val variables = object : IVariablesAndConsts { - override val blockVars: Map> - override val blockConsts: Map> - override val blockMemvars: Map> - override val subroutineVars: Map> - override val subroutineConsts: Map> - override val subroutineMemvars: Map> - init { - blockVars = mutableMapOf() - blockVars[block] = mutableSetOf(IVariablesAndConsts.StaticVariable(varInBlock.datatype, varInBlock.scopedName, varInBlock.value, varInBlock.arraysize?.constIndex(), varInBlock.zeropage, varInBlock.position)) - blockConsts = mutableMapOf() - blockMemvars = mutableMapOf() - subroutineVars = mutableMapOf() - subroutineVars[subroutine] = mutableSetOf( - IVariablesAndConsts.StaticVariable(varInSub.datatype, varInSub.scopedName, varInSub.value, varInSub.arraysize?.constIndex(), varInSub.zeropage, varInSub.position), - IVariablesAndConsts.StaticVariable(var2InSub.datatype, var2InSub.scopedName, var2InSub.value, var2InSub.arraysize?.constIndex(), var2InSub.zeropage, var2InSub.position) - ) - subroutineConsts = mutableMapOf() - subroutineMemvars = mutableMapOf() - } - } - return Pair(program, variables) + return program } - fun createTestAsmGen(program: Program, allocation: IVariablesAndConsts): AsmGen { + fun createTestAsmGen(program: Program): AsmGen { val errors = ErrorReporterForTests() val options = CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, true, C64Target()) options.compTarget.machine.zeropage = C64Zeropage(options) - return AsmGen(program, errors, allocation, options) + val st = SymbolTableMaker().makeFrom(program) + val allocation = VariableExtractor().extractFrom(program) + return AsmGen(program, errors, st, allocation, options) } "symbol and variable names from strings" { - val (program, variables) = createTestProgram() - val asmgen = createTestAsmGen(program, variables) + val program = createTestProgram() + val asmgen = createTestAsmGen(program) asmgen.asmSymbolName("name") shouldBe "name" asmgen.asmSymbolName("name") shouldBe "name" asmgen.asmSymbolName("") shouldBe "prog8_name" @@ -115,8 +98,8 @@ class TestAsmGenSymbols: StringSpec({ } "symbol and variable names from variable identifiers" { - val (program, variables) = createTestProgram() - val asmgen = createTestAsmGen(program, variables) + val program = createTestProgram() + val asmgen = createTestAsmGen(program) val sub = program.entrypoint val localvarIdent = sub.statements.asSequence().filterIsInstance().first { it.value is IdentifierReference }.value as IdentifierReference @@ -135,8 +118,8 @@ class TestAsmGenSymbols: StringSpec({ } "symbol and variable names from label identifiers" { - val (program, variables) = createTestProgram() - val asmgen = createTestAsmGen(program, variables) + val program = createTestProgram() + val asmgen = createTestAsmGen(program) val sub = program.entrypoint val localLabelIdent = (sub.statements.asSequence().filterIsInstance().first { (it.value as? AddressOf)?.identifier?.nameInSource==listOf("locallabel") }.value as AddressOf).identifier @@ -164,8 +147,8 @@ main { prog8_lib.P8ZP_SCRATCH_W1 = 1 prog8_lib.P8ZP_SCRATCH_W2 = 1 */ - val (program, variables) = createTestProgram() - val asmgen = createTestAsmGen(program, variables) + val program = createTestProgram() + val asmgen = createTestAsmGen(program) asmgen.asmSymbolName("prog8_lib.P8ZP_SCRATCH_REG") shouldBe "P8ZP_SCRATCH_REG" asmgen.asmSymbolName("prog8_lib.P8ZP_SCRATCH_W2") shouldBe "P8ZP_SCRATCH_W2" asmgen.asmSymbolName(listOf("prog8_lib","P8ZP_SCRATCH_REG")) shouldBe "P8ZP_SCRATCH_REG" diff --git a/compiler/test/helpers/compileXyz.kt b/compiler/test/helpers/compileXyz.kt index c561596d4..f99881fe1 100644 --- a/compiler/test/helpers/compileXyz.kt +++ b/compiler/test/helpers/compileXyz.kt @@ -8,6 +8,8 @@ import prog8.codegen.target.C64Target import prog8.codegen.target.c64.C64Zeropage import prog8.compiler.CompilationResult import prog8.compiler.CompilerArguments +import prog8.compiler.astprocessing.SymbolTableMaker +import prog8.compiler.astprocessing.VariableExtractor import prog8.compiler.compileProgram import prog8.compilerinterface.* import java.nio.file.Path @@ -83,11 +85,12 @@ internal fun compileText( internal fun generateAssembly( program: Program, - variables: IVariablesAndConsts, options: CompilationOptions? = null ): IAssemblyProgram? { val coptions = options ?: CompilationOptions(OutputType.RAW, CbmPrgLauncherType.BASIC, ZeropageType.DONTUSE, emptyList(), true, true, C64Target(), outputDir = outputDir) coptions.compTarget.machine.zeropage = C64Zeropage(coptions) - val asmgen = AsmGen(program, ErrorReporterForTests(), variables, coptions) + val variables = VariableExtractor().extractFrom(program) + val st = SymbolTableMaker().makeFrom(program) + val asmgen = AsmGen(program, ErrorReporterForTests(), st, variables, coptions) return asmgen.compileToAssembly() } diff --git a/compilerInterfaces/src/prog8/compilerinterface/IVariablesAndConsts.kt b/compilerInterfaces/src/prog8/compilerinterface/IVariablesAndConsts.kt index 34bd4fb54..5a24aa93a 100644 --- a/compilerInterfaces/src/prog8/compilerinterface/IVariablesAndConsts.kt +++ b/compilerInterfaces/src/prog8/compilerinterface/IVariablesAndConsts.kt @@ -13,6 +13,9 @@ import prog8.ast.statements.ZeropageWish * * note: the string variables are in here as well, they're in blockVars for the block named 'prog8_interned_strings'. */ + +// TODO remove this, and replace with SymbolTable + interface IVariablesAndConsts { data class ConstantNumberSymbol(val type: DataType, val scopedname: List, val value: Double, val position: Position) data class MemoryMappedVariable(val type: DataType, val scopedname: List, val address: UInt, val position: Position) diff --git a/compilerInterfaces/src/prog8/compilerinterface/SymbolTable.kt b/compilerInterfaces/src/prog8/compilerinterface/SymbolTable.kt new file mode 100644 index 000000000..29f0857a0 --- /dev/null +++ b/compilerInterfaces/src/prog8/compilerinterface/SymbolTable.kt @@ -0,0 +1,143 @@ +package prog8.compilerinterface + +import prog8.ast.Node +import prog8.ast.base.DataType +import prog8.ast.base.Position +import prog8.ast.expressions.Expression +import prog8.ast.statements.ZeropageWish +import prog8.ast.toHex + + +enum class StNodeType { + GLOBAL, + // MODULE, // not used with current scoping rules + BLOCK, + SUBROUTINE, + LABEL, + STATICVAR, + MEMVAR, + CONSTANT, + BUILTINFUNC +} + +open class StNode(val name: String, + val type: StNodeType, + val position: Position, + val children: MutableMap = mutableMapOf() +) { + + lateinit var parent: StNode + + val scopedName: List by lazy { + if(type== StNodeType.GLOBAL) + emptyList() + else + parent.scopedName + name + } + + fun lookup(name: String) = + lookupUnqualified(name) + fun lookup(scopedName: List) = + if(scopedName.size>1) lookupQualified(scopedName) else lookupUnqualified(scopedName[0]) + fun lookupOrElse(name: String, default: () -> StNode) = + lookupUnqualified(name) ?: default() + fun lookupOrElse(scopedName: List, default: () -> StNode) = + lookup(scopedName) ?: default() + + private fun lookupQualified(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 + } + + private fun lookupUnqualified(name: String): StNode? { + // first consider the builtin functions + var globalscope = this + while(globalscope.type!=StNodeType.GLOBAL) + globalscope = globalscope.parent + val globalNode = globalscope.children[name] + if(globalNode!=null && globalNode.type==StNodeType.BUILTINFUNC) + return globalNode + + // search for the unqualified name in the current scope or its parent scopes + var scope=this + while(true) { + val node = scope.children[name] + if(node!=null) + return node + if(scope.type==StNodeType.GLOBAL) + return null + else + scope = scope.parent + } + } + + fun printIndented(indent: Int) { + print(" ".repeat(indent)) + when(type) { + StNodeType.GLOBAL -> print("SYMBOL-TABLE:") + StNodeType.BLOCK -> print("(B) ") + StNodeType.SUBROUTINE -> print("(S) ") + StNodeType.LABEL -> print("(L) ") + StNodeType.STATICVAR -> print("(V) ") + StNodeType.MEMVAR -> print("(M) ") + StNodeType.CONSTANT -> print("(C) ") + StNodeType.BUILTINFUNC -> print("(F) ") + } + printProperties() + println() + children.forEach { (_, node) -> node.printIndented(indent+1) } + } + + open fun printProperties() { + print("$name ") + } + + fun add(child: StNode) { + children[child.name] = child + child.parent = this + } +} + +class SymbolTable : StNode("", StNodeType.GLOBAL, Position.DUMMY) { + fun print() = printIndented(0) + + override fun printProperties() { } + + val origAstLinks = mutableMapOf() // link the original Ast nodes into the table. TODO is this really needed? +} + +class StStaticVariable(name: String, + val dt: DataType, + val initialvalue: Expression?, + val arraysize: Int?, + val zpw: ZeropageWish, + position: Position) : StNode(name, StNodeType.STATICVAR, position) { + override fun printProperties() { + print("$name dt=$dt initialval=$initialvalue arraysize=$arraysize zpw=$zpw") + } +} + +class StConstant(name: String, val dt: DataType, val value: Double, position: Position) : + StNode(name, StNodeType.CONSTANT, position) { + override fun printProperties() { + print("$name dt=$dt value=$value") + } +} + +class StMemVar(name: String, val dt: DataType, val address: UInt, position: Position) : + StNode(name, StNodeType.MEMVAR, position +) { + override fun printProperties() { + print("$name dt=$dt address=${address.toHex()}") + } +}