mirror of
https://github.com/irmen/prog8.git
synced 2024-12-23 09:32:43 +00:00
working on symboltable
This commit is contained in:
parent
859ab36347
commit
496245c801
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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<String, StNode> = mutableMapOf()
|
||||
) {
|
||||
|
||||
lateinit var parent: StNode
|
||||
|
||||
val scopedName: List<String> 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()}")
|
||||
}
|
||||
}
|
@ -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}")
|
||||
|
@ -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<StNode>()
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
@ -19,7 +19,7 @@ internal class VariableExtractor: IAstVisitor {
|
||||
private val allSubroutineConsts = mutableMapOf<Subroutine, MutableSet<VarDecl>>()
|
||||
private val allSubroutineMemoryvars = mutableMapOf<Subroutine, MutableSet<VarDecl>>()
|
||||
|
||||
fun extractVars(program: Program): IVariablesAndConsts {
|
||||
fun extractFrom(program: Program): IVariablesAndConsts {
|
||||
this.visit(program)
|
||||
return VariablesAndConsts(
|
||||
allBlockVars, allBlockConsts, allBlockMemoryvars,
|
||||
|
@ -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()
|
||||
}
|
||||
|
93
compiler/test/TestSymbolTable.kt
Normal file
93
compiler/test/TestSymbolTable.kt
Normal file
@ -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
|
||||
}
|
@ -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<Program, IVariablesAndConsts> {
|
||||
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<Block, Set<IVariablesAndConsts.StaticVariable>>
|
||||
override val blockConsts: Map<Block, Set<IVariablesAndConsts.ConstantNumberSymbol>>
|
||||
override val blockMemvars: Map<Block, Set<IVariablesAndConsts.MemoryMappedVariable>>
|
||||
override val subroutineVars: Map<Subroutine, Set<IVariablesAndConsts.StaticVariable>>
|
||||
override val subroutineConsts: Map<Subroutine, Set<IVariablesAndConsts.ConstantNumberSymbol>>
|
||||
override val subroutineMemvars: Map<Subroutine, Set<IVariablesAndConsts.MemoryMappedVariable>>
|
||||
|
||||
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("<name>") 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<Assignment>().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<Assignment>().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"
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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<String>, val value: Double, val position: Position)
|
||||
data class MemoryMappedVariable(val type: DataType, val scopedname: List<String>, val address: UInt, val position: Position)
|
||||
|
143
compilerInterfaces/src/prog8/compilerinterface/SymbolTable.kt
Normal file
143
compilerInterfaces/src/prog8/compilerinterface/SymbolTable.kt
Normal file
@ -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<String, StNode> = mutableMapOf()
|
||||
) {
|
||||
|
||||
lateinit var parent: StNode
|
||||
|
||||
val scopedName: List<String> by lazy {
|
||||
if(type== StNodeType.GLOBAL)
|
||||
emptyList()
|
||||
else
|
||||
parent.scopedName + name
|
||||
}
|
||||
|
||||
fun lookup(name: String) =
|
||||
lookupUnqualified(name)
|
||||
fun lookup(scopedName: List<String>) =
|
||||
if(scopedName.size>1) lookupQualified(scopedName) else lookupUnqualified(scopedName[0])
|
||||
fun lookupOrElse(name: String, default: () -> StNode) =
|
||||
lookupUnqualified(name) ?: default()
|
||||
fun lookupOrElse(scopedName: List<String>, default: () -> StNode) =
|
||||
lookup(scopedName) ?: default()
|
||||
|
||||
private fun lookupQualified(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
|
||||
}
|
||||
|
||||
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<Node, StNode>() // 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()}")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user