working on symboltable

This commit is contained in:
Irmen de Jong 2022-03-04 23:25:26 +01:00
parent 859ab36347
commit 496245c801
13 changed files with 294 additions and 143 deletions

View File

@ -23,6 +23,7 @@ internal const val subroutineFloatEvalResultVar2 = "prog8_float_eval_result2"
class AsmGen(internal val program: Program, class AsmGen(internal val program: Program,
internal val errors: IErrorReporter, internal val errors: IErrorReporter,
internal val symbolTable: SymbolTable,
internal val variables: IVariablesAndConsts, internal val variables: IVariablesAndConsts,
internal val options: CompilationOptions): IAssemblyGenerator { internal val options: CompilationOptions): IAssemblyGenerator {
@ -37,7 +38,7 @@ class AsmGen(internal val program: Program,
private val postincrdecrAsmGen = PostIncrDecrAsmGen(program, this) private val postincrdecrAsmGen = PostIncrDecrAsmGen(program, this)
private val functioncallAsmGen = FunctionCallAsmGen(program, this) private val functioncallAsmGen = FunctionCallAsmGen(program, this)
private val expressionsAsmGen = ExpressionsAsmGen(program, this, allocator) 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 assignmentAsmGen = AssignmentAsmGen(program, this, allocator)
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen, allocator) private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen, allocator)

View File

@ -24,6 +24,7 @@ import kotlin.math.absoluteValue
internal class ProgramAndVarsGen( internal class ProgramAndVarsGen(
val program: Program, val program: Program,
val variables: IVariablesAndConsts, val variables: IVariablesAndConsts,
val symboltable: SymbolTable,
val options: CompilationOptions, val options: CompilationOptions,
val errors: IErrorReporter, val errors: IErrorReporter,
private val functioncallAsmGen: FunctionCallAsmGen, private val functioncallAsmGen: FunctionCallAsmGen,
@ -383,6 +384,8 @@ internal class ProgramAndVarsGen(
} }
private fun zeropagevars2asm(block: Block) { 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() val varnames = variables.blockVars.getOrDefault(block, emptySet()).map { it.scopedname }.toSet()
zeropagevars2asm(varnames) zeropagevars2asm(varnames)
} }

View File

@ -5,6 +5,7 @@ import prog8.compilerinterface.*
class AsmGen(internal val program: Program, class AsmGen(internal val program: Program,
internal val errors: IErrorReporter, internal val errors: IErrorReporter,
internal val symbolTable: SymbolTable,
internal val variables: IVariablesAndConsts, internal val variables: IVariablesAndConsts,
internal val options: CompilationOptions): IAssemblyGenerator { internal val options: CompilationOptions): IAssemblyGenerator {
@ -12,10 +13,9 @@ class AsmGen(internal val program: Program,
println("\n** experimental 65(c)02 code generator **\n") println("\n** experimental 65(c)02 code generator **\n")
val stMaker = SymbolTableMaker()
val symbolTable = stMaker.make(program)
symbolTable.print() symbolTable.print()
println("..todo: create assembly code into ${options.outputDir.toAbsolutePath()}..") println("..todo: create assembly code into ${options.outputDir.toAbsolutePath()}..")
return AssemblyProgram("dummy") return AssemblyProgram("dummy")
} }

View File

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

View File

@ -363,7 +363,8 @@ private fun writeAssembly(program: Program,
compilerOptions.compTarget.machine.initializeZeropage(compilerOptions) compilerOptions.compTarget.machine.initializeZeropage(compilerOptions)
program.processAstBeforeAsmGeneration(compilerOptions, errors) program.processAstBeforeAsmGeneration(compilerOptions, errors)
errors.report() 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, // 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. // 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 *************") // println("*********** AST RIGHT BEFORE ASM GENERATION *************")
// printProgram(program) // printProgram(program)
val assembly = asmGeneratorFor(program, errors, variables, compilerOptions).compileToAssembly() val assembly = asmGeneratorFor(program, errors, symbolTable, variables, compilerOptions).compileToAssembly()
errors.report() errors.report()
return if(assembly!=null && errors.noErrors()) { return if(assembly!=null && errors.noErrors()) {
@ -414,14 +415,18 @@ fun printProgram(program: Program) {
println() 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.experimentalCodegen) {
if (options.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02)) 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 { } else {
if (options.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02)) 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}") throw NotImplementedError("no asm generator for cpu ${options.compTarget.machine.cpu}")

View File

@ -1,4 +1,4 @@
package prog8.codegen.experimental6502 package prog8.compiler.astprocessing
import prog8.ast.Program import prog8.ast.Program
import prog8.ast.base.Position import prog8.ast.base.Position
@ -9,57 +9,57 @@ import prog8.ast.statements.Label
import prog8.ast.statements.Subroutine import prog8.ast.statements.Subroutine
import prog8.ast.statements.VarDecl import prog8.ast.statements.VarDecl
import prog8.ast.walk.IAstVisitor import prog8.ast.walk.IAstVisitor
import prog8.compilerinterface.*
import java.util.* import java.util.*
class SymbolTableMaker: IAstVisitor { internal class SymbolTableMaker: IAstVisitor {
private val st = SymbolTable() private val st = SymbolTable()
private val scopestack = Stack<StNode>() private val scopestack = Stack<StNode>()
fun make(program: Program): SymbolTable { fun makeFrom(program: Program): SymbolTable {
scopestack.clear() scopestack.clear()
st.children.clear() st.children.clear()
this.visit(program) this.visit(program)
program.builtinFunctions.names.forEach { program.builtinFunctions.names.forEach {
val node = StNode(it, StNodeType.BUILTINFUNC, Position.DUMMY) val node = StNode(it, StNodeType.BUILTINFUNC, Position.DUMMY)
node.parent = st st.add(node)
st.children[it] = node
} }
return st return st
} }
override fun visit(block: Block) { override fun visit(block: Block) {
val node = StNode(block.name, StNodeType.BLOCK, block.position) val node = StNode(block.name, StNodeType.BLOCK, block.position)
node.parent = st
scopestack.push(node) scopestack.push(node)
super.visit(block) super.visit(block)
scopestack.pop() scopestack.pop()
st.children[node.name] = node st.add(node)
st.origAstLinks[block] = node
} }
override fun visit(subroutine: Subroutine) { override fun visit(subroutine: Subroutine) {
val node = StNode(subroutine.name, StNodeType.SUBROUTINE, subroutine.position) val node = StNode(subroutine.name, StNodeType.SUBROUTINE, subroutine.position)
node.parent = scopestack.peek()
scopestack.push(node) scopestack.push(node)
super.visit(subroutine) super.visit(subroutine)
scopestack.pop() scopestack.pop()
scopestack.peek().children[node.name] = node scopestack.peek().add(node)
st.origAstLinks[subroutine] = node
} }
override fun visit(decl: VarDecl) { override fun visit(decl: VarDecl) {
val node = val node =
when(decl.type) { 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.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) VarDeclType.MEMORY -> StMemVar(decl.name, decl.datatype, (decl.value as NumericLiteral).number.toUInt(), decl.position)
} }
node.parent = scopestack.peek() scopestack.peek().add(node)
node.parent.children[node.name] = node st.origAstLinks[decl] = node
} }
override fun visit(label: Label) { override fun visit(label: Label) {
val node = StNode(label.name, StNodeType.LABEL, label.position) val node = StNode(label.name, StNodeType.LABEL, label.position)
node.parent = scopestack.peek() scopestack.peek().add(node)
node.parent.children[node.name] = node st.origAstLinks[label] = node
} }
} }

View File

@ -19,7 +19,7 @@ internal class VariableExtractor: IAstVisitor {
private val allSubroutineConsts = mutableMapOf<Subroutine, MutableSet<VarDecl>>() private val allSubroutineConsts = mutableMapOf<Subroutine, MutableSet<VarDecl>>()
private val allSubroutineMemoryvars = mutableMapOf<Subroutine, MutableSet<VarDecl>>() private val allSubroutineMemoryvars = mutableMapOf<Subroutine, MutableSet<VarDecl>>()
fun extractVars(program: Program): IVariablesAndConsts { fun extractFrom(program: Program): IVariablesAndConsts {
this.visit(program) this.visit(program)
return VariablesAndConsts( return VariablesAndConsts(
allBlockVars, allBlockConsts, allBlockMemoryvars, allBlockVars, allBlockConsts, allBlockMemoryvars,

View File

@ -290,7 +290,6 @@ class TestOptimization: FunSpec({
} }
""" """
val result = compileText(C64Target(), false, src, writeAssembly = false).assertSuccess() val result = compileText(C64Target(), false, src, writeAssembly = false).assertSuccess()
val variables = VariableExtractor().extractVars(result.program)
// bb = (( not bb as uword) or not ww) // bb = (( not bb as uword) or not ww)
val bbAssign = result.program.entrypoint.statements.last() as Assignment 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.right as PrefixExpression).expression as? IdentifierReference)?.nameInSource shouldBe listOf("ww")
bbAssigns1expr.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE 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 shouldNotBe null
asm!!.name.shouldNotBeBlank() asm!!.name.shouldNotBeBlank()
} }

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

View File

@ -15,6 +15,8 @@ import prog8.ast.statements.*
import prog8.codegen.cpu6502.AsmGen import prog8.codegen.cpu6502.AsmGen
import prog8.codegen.target.C64Target import prog8.codegen.target.C64Target
import prog8.codegen.target.c64.C64Zeropage import prog8.codegen.target.c64.C64Zeropage
import prog8.compiler.astprocessing.SymbolTableMaker
import prog8.compiler.astprocessing.VariableExtractor
import prog8.compilerinterface.* import prog8.compilerinterface.*
import prog8.parser.SourceCode import prog8.parser.SourceCode
import prog8tests.helpers.DummyFunctions import prog8tests.helpers.DummyFunctions
@ -23,7 +25,7 @@ import prog8tests.helpers.DummyStringEncoder
import prog8tests.helpers.ErrorReporterForTests import prog8tests.helpers.ErrorReporterForTests
class TestAsmGenSymbols: StringSpec({ class TestAsmGenSymbols: StringSpec({
fun createTestProgram(): Pair<Program, IVariablesAndConsts> { fun createTestProgram(): Program {
/* /*
main { main {
@ -69,41 +71,22 @@ class TestAsmGenSymbols: StringSpec({
val module = Module(mutableListOf(block), Position.DUMMY, SourceCode.Generated("test")) val module = Module(mutableListOf(block), Position.DUMMY, SourceCode.Generated("test"))
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder).addModule(module) 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 { return program
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)
} }
fun createTestAsmGen(program: Program, allocation: IVariablesAndConsts): AsmGen { fun createTestAsmGen(program: Program): AsmGen {
val errors = ErrorReporterForTests() val errors = ErrorReporterForTests()
val options = CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, true, C64Target()) val options = CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, true, C64Target())
options.compTarget.machine.zeropage = C64Zeropage(options) 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" { "symbol and variable names from strings" {
val (program, variables) = createTestProgram() val program = createTestProgram()
val asmgen = createTestAsmGen(program, variables) val asmgen = createTestAsmGen(program)
asmgen.asmSymbolName("name") shouldBe "name" asmgen.asmSymbolName("name") shouldBe "name"
asmgen.asmSymbolName("name") shouldBe "name" asmgen.asmSymbolName("name") shouldBe "name"
asmgen.asmSymbolName("<name>") shouldBe "prog8_name" asmgen.asmSymbolName("<name>") shouldBe "prog8_name"
@ -115,8 +98,8 @@ class TestAsmGenSymbols: StringSpec({
} }
"symbol and variable names from variable identifiers" { "symbol and variable names from variable identifiers" {
val (program, variables) = createTestProgram() val program = createTestProgram()
val asmgen = createTestAsmGen(program, variables) val asmgen = createTestAsmGen(program)
val sub = program.entrypoint val sub = program.entrypoint
val localvarIdent = sub.statements.asSequence().filterIsInstance<Assignment>().first { it.value is IdentifierReference }.value as IdentifierReference 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" { "symbol and variable names from label identifiers" {
val (program, variables) = createTestProgram() val program = createTestProgram()
val asmgen = createTestAsmGen(program, variables) val asmgen = createTestAsmGen(program)
val sub = program.entrypoint val sub = program.entrypoint
val localLabelIdent = (sub.statements.asSequence().filterIsInstance<Assignment>().first { (it.value as? AddressOf)?.identifier?.nameInSource==listOf("locallabel") }.value as AddressOf).identifier 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_W1 = 1
prog8_lib.P8ZP_SCRATCH_W2 = 1 prog8_lib.P8ZP_SCRATCH_W2 = 1
*/ */
val (program, variables) = createTestProgram() val program = createTestProgram()
val asmgen = createTestAsmGen(program, variables) val asmgen = createTestAsmGen(program)
asmgen.asmSymbolName("prog8_lib.P8ZP_SCRATCH_REG") shouldBe "P8ZP_SCRATCH_REG" asmgen.asmSymbolName("prog8_lib.P8ZP_SCRATCH_REG") shouldBe "P8ZP_SCRATCH_REG"
asmgen.asmSymbolName("prog8_lib.P8ZP_SCRATCH_W2") shouldBe "P8ZP_SCRATCH_W2" asmgen.asmSymbolName("prog8_lib.P8ZP_SCRATCH_W2") shouldBe "P8ZP_SCRATCH_W2"
asmgen.asmSymbolName(listOf("prog8_lib","P8ZP_SCRATCH_REG")) shouldBe "P8ZP_SCRATCH_REG" asmgen.asmSymbolName(listOf("prog8_lib","P8ZP_SCRATCH_REG")) shouldBe "P8ZP_SCRATCH_REG"

View File

@ -8,6 +8,8 @@ import prog8.codegen.target.C64Target
import prog8.codegen.target.c64.C64Zeropage import prog8.codegen.target.c64.C64Zeropage
import prog8.compiler.CompilationResult import prog8.compiler.CompilationResult
import prog8.compiler.CompilerArguments import prog8.compiler.CompilerArguments
import prog8.compiler.astprocessing.SymbolTableMaker
import prog8.compiler.astprocessing.VariableExtractor
import prog8.compiler.compileProgram import prog8.compiler.compileProgram
import prog8.compilerinterface.* import prog8.compilerinterface.*
import java.nio.file.Path import java.nio.file.Path
@ -83,11 +85,12 @@ internal fun compileText(
internal fun generateAssembly( internal fun generateAssembly(
program: Program, program: Program,
variables: IVariablesAndConsts,
options: CompilationOptions? = null options: CompilationOptions? = null
): IAssemblyProgram? { ): IAssemblyProgram? {
val coptions = options ?: CompilationOptions(OutputType.RAW, CbmPrgLauncherType.BASIC, ZeropageType.DONTUSE, emptyList(), true, true, C64Target(), outputDir = outputDir) val coptions = options ?: CompilationOptions(OutputType.RAW, CbmPrgLauncherType.BASIC, ZeropageType.DONTUSE, emptyList(), true, true, C64Target(), outputDir = outputDir)
coptions.compTarget.machine.zeropage = C64Zeropage(coptions) 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() return asmgen.compileToAssembly()
} }

View File

@ -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'. * 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 { interface IVariablesAndConsts {
data class ConstantNumberSymbol(val type: DataType, val scopedname: List<String>, val value: Double, val position: Position) 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) data class MemoryMappedVariable(val type: DataType, val scopedname: List<String>, val address: UInt, val position: Position)

View 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()}")
}
}