mirror of
https://github.com/irmen/prog8.git
synced 2024-12-24 16:29:21 +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,
|
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)
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
|
@ -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)
|
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}")
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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,
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
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.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"
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
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