lookup via new ST

This commit is contained in:
Irmen de Jong 2023-02-04 00:02:50 +01:00
parent 109e118aba
commit c75b1581d2
23 changed files with 369 additions and 291 deletions

View File

@ -1,5 +1,6 @@
package prog8.code
import prog8.code.ast.PtNode
import prog8.code.core.*
@ -7,7 +8,7 @@ import prog8.code.core.*
* Tree structure containing all symbol definitions in the program
* (blocks, subroutines, variables (all types), memoryslabs, and labels).
*/
class SymbolTable : StNode("", StNodeType.GLOBAL, Position.DUMMY) {
class SymbolTable(astNode: PtNode) : StNode("", StNodeType.GLOBAL, Position.DUMMY, astNode) {
/**
* The table as a flat mapping of scoped names to the StNode.
* This gives the fastest lookup possible (no need to traverse tree nodes)
@ -77,6 +78,7 @@ enum class StNodeType {
open class StNode(val name: String,
val type: StNodeType,
val position: Position,
val astNode: PtNode, // TODO keep reference to the node in the AST
val children: MutableMap<String, StNode> = mutableMapOf()
) {
@ -151,7 +153,8 @@ class StStaticVariable(name: String,
val onetimeInitializationArrayValue: StArray?,
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
val zpwish: ZeropageWish,
position: Position) : StNode(name, StNodeType.STATICVAR, position) {
astNode: PtNode,
position: Position) : StNode(name, StNodeType.STATICVAR, position, astNode = astNode) {
init {
if(bss) {
@ -180,8 +183,8 @@ class StStaticVariable(name: String,
}
class StConstant(name: String, val dt: DataType, val value: Double, position: Position) :
StNode(name, StNodeType.CONSTANT, position) {
class StConstant(name: String, val dt: DataType, val value: Double, astNode: PtNode, position: Position) :
StNode(name, StNodeType.CONSTANT, position, astNode) {
}
@ -189,21 +192,23 @@ class StMemVar(name: String,
val dt: DataType,
val address: UInt,
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
astNode: PtNode,
position: Position) :
StNode(name, StNodeType.MEMVAR, position) {
StNode(name, StNodeType.MEMVAR, position, astNode) {
}
class StMemorySlab(
name: String,
val size: UInt,
val align: UInt,
astNode: PtNode,
position: Position
):
StNode(name, StNodeType.MEMORYSLAB, position) {
StNode(name, StNodeType.MEMORYSLAB, position, astNode) {
}
class StSub(name: String, val parameters: List<StSubroutineParameter>, val returnType: DataType?, position: Position) :
StNode(name, StNodeType.SUBROUTINE, position) {
class StSub(name: String, val parameters: List<StSubroutineParameter>, val returnType: DataType?, astNode: PtNode, position: Position) :
StNode(name, StNodeType.SUBROUTINE, position, astNode) {
}
@ -211,9 +216,10 @@ class StRomSub(name: String,
val address: UInt,
val parameters: List<StRomSubParameter>,
val returns: List<RegisterOrStatusflag>,
astNode: PtNode,
position: Position) :
StNode(name, StNodeType.ROMSUB, position) {
}
StNode(name, StNodeType.ROMSUB, position, astNode)
class StSubroutineParameter(val name: String, val type: DataType)

View File

@ -0,0 +1,212 @@
package prog8.code
import prog8.code.ast.*
import prog8.code.core.*
import prog8.code.target.VMTarget
import java.util.*
class SymbolTableMaker(private val program: PtProgram, private val options: CompilationOptions) {
fun make(): SymbolTable {
val st = SymbolTable(program)
BuiltinFunctions.forEach {
st.add(StNode(it.key, StNodeType.BUILTINFUNC, Position.DUMMY, PtIdentifier(it.key, it.value.returnType ?: DataType.UNDEFINED, Position.DUMMY)))
}
val scopestack = Stack<StNode>()
scopestack.push(st)
program.children.forEach {
addToSt(it, scopestack)
}
require(scopestack.size==1)
if(options.compTarget.name != VMTarget.NAME) {
listOf(
PtMemMapped("P8ZP_SCRATCH_B1", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_B1, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_REG", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_REG, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_W1", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W1, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_W2", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W2, Position.DUMMY),
PtMemMapped("P8ESTACK_LO", DataType.UBYTE, options.compTarget.machine.ESTACK_LO, Position.DUMMY),
PtMemMapped("P8ESTACK_HI", DataType.UBYTE, options.compTarget.machine.ESTACK_HI, Position.DUMMY)
).forEach {
st.add(StMemVar(it.name, it.type, it.address, null, it, Position.DUMMY))
}
}
return st
}
// TODO INITIAL VALUES / BSS
private fun addToSt(node: PtNode, scope: Stack<StNode>) {
val stNode = when(node) {
is PtAsmSub -> {
if(node.address==null) {
val params = node.parameters.map { StSubroutineParameter(it.first.name, it.first.type) }
StSub(node.name, params, node.returnTypes.singleOrNull(), node, node.position)
} else {
val parameters = node.parameters.map { StRomSubParameter(it.second, it.first.type) }
StRomSub(node.name, node.address, parameters, node.retvalRegisters, node, node.position)
}
}
is PtBlock -> {
StNode(node.name, StNodeType.BLOCK, node.position, node)
}
is PtConstant -> {
StConstant(node.name, node.type, node.value, node, node.position)
}
is PtLabel -> {
StNode(node.name, StNodeType.LABEL, node.position, node)
}
is PtMemMapped -> {
StMemVar(node.name, node.type, node.address, null, node, node.position) // TODO missing node.length
}
is PtSub -> {
val params = node.parameters.map {StSubroutineParameter(it.name, it.type) }
StSub(node.name, params, node.returntype, node, node.position)
}
is PtVariable -> {
val bss = when (node.type) {
// TODO should bss be a computed property on PtVariable?
DataType.STR -> false
in ArrayDatatypes -> node.value==null || node.arraySize==0u
else -> node.value==null
}
val initialNumeric: Double?
val initialString: StString?
val initialArray: StArray?
val numElements: Int?
val value = node.value
if(value!=null) {
initialNumeric = (value as? PtNumber)?.number
when (value) {
is PtString -> {
initialString = StString(value.value, value.encoding)
initialArray = null
numElements = value.value.length + 1 // include the terminating 0-byte
}
is PtArray -> {
initialArray = makeInitialArray(value)
initialString = null
numElements = initialArray.size
require(node.arraySize?.toInt()==numElements)
}
else -> {
initialString = null
initialArray = null
numElements = node.arraySize?.toInt()
}
}
} else {
initialNumeric = null
initialArray = null
initialString = null
numElements = node.arraySize?.toInt()
}
val zeropage = ZeropageWish.DONTCARE // TODO how, can this be removed from the ST perhaps? Or is it required in the variable allocator later
StStaticVariable(node.name, node.type, bss, initialNumeric, initialString, initialArray, numElements, zeropage, node, node.position)
}
is PtBuiltinFunctionCall -> {
if(node.name=="memory") {
// memory slab allocations are a builtin functioncall in the program, but end up named as well in the symboltable
require(node.name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name"}
val slabname = (node.args[0] as PtString).value
val size = (node.args[1] as PtNumber).number.toUInt()
val align = (node.args[2] as PtNumber).number.toUInt()
// don't add memory slabs in nested scope, just put them in the top level of the ST
scope.firstElement().add(StMemorySlab("prog8_memoryslab_$slabname", size, align, node, node.position))
}
null
}
else -> null // node is not present in the ST
}
if(stNode!=null) {
scope.peek().add(stNode)
scope.push(stNode)
}
node.children.forEach {
addToSt(it, scope)
}
if(stNode!=null)
scope.pop()
}
private fun makeInitialArray(value: PtArray): List<StArrayElement> {
return value.children.map {
when(it) {
is PtAddressOf -> StArrayElement(null, it.identifier.name)
is PtIdentifier -> StArrayElement(null, it.name)
is PtNumber -> StArrayElement(it.number, null)
else -> throw AssemblyError("invalid array element $it")
}
}
}
}
// override fun visit(decl: VarDecl) {
// val node =
// when(decl.type) {
// VarDeclType.VAR -> {
// var initialNumeric = (decl.value as? NumericLiteral)?.number
// if(initialNumeric==0.0)
// initialNumeric=null // variable will go into BSS and this will be set to 0
// val initialStringLit = decl.value as? StringLiteral
// val initialString = if(initialStringLit==null) null else Pair(initialStringLit.value, initialStringLit.encoding)
// val initialArrayLit = decl.value as? ArrayLiteral
// val initialArray = makeInitialArray(initialArrayLit)
// if(decl.isArray && decl.datatype !in ArrayDatatypes)
// throw FatalAstException("array vardecl has mismatched dt ${decl.datatype}")
// val numElements =
// if(decl.isArray)
// decl.arraysize!!.constIndex()
// else if(initialStringLit!=null)
// initialStringLit.value.length+1 // include the terminating 0-byte
// else
// null
// val bss = if(decl.datatype==DataType.STR)
// false
// else if(decl.isArray)
// initialArray.isNullOrEmpty()
// else
// initialNumeric == null
// val astNode = PtVariable(decl.name, decl.datatype, null, null, decl.position)
// StStaticVariable(decl.name, decl.datatype, bss, initialNumeric, initialString, initialArray, numElements, decl.zeropage, astNode, decl.position)
// }
// VarDeclType.CONST -> {
// val astNode = PtVariable(decl.name, decl.datatype, null, null, decl.position)
// StConstant(decl.name, decl.datatype, (decl.value as NumericLiteral).number, astNode, decl.position)
// }
// VarDeclType.MEMORY -> {
// val numElements =
// if(decl.datatype in ArrayDatatypes)
// decl.arraysize!!.constIndex()
// else null
// val astNode = PtVariable(decl.name, decl.datatype, null, null, decl.position)
// StMemVar(decl.name, decl.datatype, (decl.value as NumericLiteral).number.toUInt(), numElements, astNode, decl.position)
// }
// }
// scopestack.peek().add(node)
// // st.origAstLinks[decl] = node
// }
//
// private fun makeInitialArray(arrayLit: ArrayLiteral?): StArray? {
// if(arrayLit==null)
// return null
// return arrayLit.value.map {
// when(it){
// is AddressOf -> {
// val scopedName = it.identifier.targetNameAndType(program).first
// StArrayElement(null, scopedName)
// }
// is IdentifierReference -> {
// val scopedName = it.targetNameAndType(program).first
// StArrayElement(null, scopedName)
// }
// is NumericLiteral -> StArrayElement(it.number, null)
// else -> throw FatalAstException("weird element dt in array literal")
// }
// }.toList()
// }
//

View File

@ -1,8 +1,6 @@
package prog8.code.ast
import prog8.code.core.DataType
import prog8.code.core.Encoding
import prog8.code.core.Position
import prog8.code.core.*
import java.util.*
import kotlin.math.round
@ -66,11 +64,15 @@ class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) {
}
class PtArrayIndexer(type: DataType, position: Position): PtExpression(type, position) {
class PtArrayIndexer(elementType: DataType, position: Position): PtExpression(elementType, position) {
val variable: PtIdentifier
get() = children[0] as PtIdentifier
val index: PtExpression
get() = children[1] as PtExpression
init {
require(elementType in NumericDatatypes)
}
}
@ -82,7 +84,8 @@ class PtArray(type: DataType, position: Position): PtExpression(type, position)
return type==other.type && children == other.children
}
val size: Int = children.size
val size: Int
get() = children.size
}

View File

@ -214,6 +214,7 @@ class PtConstant(name: String, override val type: DataType, val value: Double, p
}
// TODO what about memory mapped arrays that have a length? missing property!
class PtMemMapped(name: String, override val type: DataType, val address: UInt, position: Position) : PtNamedNode(name, position), IPtVariable {
override fun printProperties() {
print("&$type $name = ${address.toHex()}")

View File

@ -107,7 +107,8 @@ class AsmGen(
internal fun loadByteFromPointerIntoA(pointervar: PtIdentifier): String {
// returns the source name of the zero page pointervar if it's already in the ZP,
// otherwise returns "P8ZP_SCRATCH_W1" which is the intermediary
when (val target = pointervar.targetStatement(program)) {
val symbol = symbolTable.lookup(pointervar.name)
when (val target = symbol!!.astNode) {
is PtLabel -> {
val sourceName = asmSymbolName(pointervar)
out(" lda $sourceName")
@ -559,7 +560,8 @@ class AsmGen(
}
}
is PtIdentifier -> {
val vardecl = (stmt.count as PtIdentifier).targetStatement(program) as PtVariable
val symbol = symbolTable.lookup((stmt.count as PtIdentifier).name)
val vardecl = symbol!!.astNode as PtVariable
val name = asmVariableName(stmt.count as PtIdentifier)
when(vardecl.type) {
DataType.UBYTE, DataType.BYTE -> {
@ -820,8 +822,8 @@ $repeatLabel lda $counterVar
return when {
ident!=null -> {
// can be a label, or a pointer variable
val target = ident.targetVarDecl(program)
if(target!=null)
val symbol = symbolTable.lookup(ident.name)
if(symbol!=null)
Pair(asmSymbolName(ident), true) // indirect
else
Pair(asmSymbolName(ident), false)
@ -989,10 +991,11 @@ $repeatLabel lda $counterVar
val ptrAndIndex = pointerViaIndexRegisterPossible(expr)
if(ptrAndIndex!=null) {
val pointervar = ptrAndIndex.first as? PtIdentifier
when(pointervar?.targetStatement(program)) {
val target = if(pointervar==null) null else symbolTable.lookup(pointervar.name)!!.astNode
when(target) {
is PtLabel -> {
assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y)
out(" lda ${asmSymbolName(pointervar)},y")
out(" lda ${asmSymbolName(pointervar!!)},y")
return true
}
is IPtVariable, null -> {

View File

@ -52,15 +52,17 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
require(fcall.args[0] is PtIdentifier) {
"attempt to pop a value into a differently typed variable, or in something else that isn't supported ${fcall.position}"
}
val target = (fcall.args[0] as PtIdentifier).targetVarDecl(program)
asmgen.popCpuStack(DataType.UBYTE, target!!, fcall.definingISub())
val symbol = asmgen.symbolTable.lookup((fcall.args[0] as PtIdentifier).name)
val target = symbol!!.astNode as IPtVariable
asmgen.popCpuStack(DataType.UBYTE, target, fcall.definingISub())
}
"popw" -> {
require(fcall.args[0] is PtIdentifier) {
"attempt to pop a value into a differently typed variable, or in something else that isn't supported ${fcall.position}"
}
val target = (fcall.args[0] as PtIdentifier).targetVarDecl(program)
asmgen.popCpuStack(DataType.UWORD, target!!, fcall.definingISub())
val symbol = asmgen.symbolTable.lookup((fcall.args[0] as PtIdentifier).name)
val target = symbol!!.astNode as IPtVariable
asmgen.popCpuStack(DataType.UWORD, target, fcall.definingISub())
}
"rsave" -> funcRsave()
"rsavex" -> funcRsaveX()
@ -349,7 +351,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcReverse(fcall: PtBuiltinFunctionCall) {
val variable = fcall.args.single()
if (variable is PtIdentifier) {
val decl = variable.targetVarDecl(program) as PtVariable
val symbol = asmgen.symbolTable.lookup(variable.name)
val decl = symbol!!.astNode as PtVariable
val varName = asmgen.asmVariableName(variable)
val numElements = decl.arraySize!!
when (decl.type) {
@ -388,7 +391,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcSort(fcall: PtBuiltinFunctionCall) {
val variable = fcall.args.single()
if (variable is PtIdentifier) {
val decl = variable.targetVarDecl(program) as PtVariable
val symbol = asmgen.symbolTable.lookup(variable.name)
val decl = symbol!!.astNode as PtVariable
val varName = asmgen.asmVariableName(variable)
val numElements = decl.arraySize!!
when (decl.type) {
@ -1007,7 +1011,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun outputAddressAndLenghtOfArray(arg: PtExpression) {
// address in P8ZP_SCRATCH_W1, number of elements in A
arg as PtIdentifier
val arrayVar = arg.targetVarDecl(program)!! as PtVariable
val symbol = asmgen.symbolTable.lookup(arg.name)
val arrayVar = symbol!!.astNode as PtVariable
if(arrayVar.arraySize==null)
throw AssemblyError("length of non-array requested")
val size = arrayVar.arraySize!!

View File

@ -44,7 +44,8 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
private fun translateFunctionCallResultOntoStack(call: PtFunctionCall) {
// only for use in nested expression evaluation
val sub = call.targetSubroutine(program)
val symbol = asmgen.symbolTable.lookup(call.name)
val sub = symbol!!.astNode as IPtSubroutine
asmgen.saveXbeforeCall(call)
asmgen.translateFunctionCall(call, true)
if(sub.regXasResult()) {
@ -725,7 +726,7 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
val elementDt = arrayExpr.type
val arrayVarName = asmgen.asmVariableName(arrayExpr.variable)
if(arrayExpr.type==DataType.UWORD) {
if(arrayExpr.variable.type==DataType.UWORD) {
// indexing a pointer var instead of a real array or string
if(elementDt !in ByteDatatypes)
throw AssemblyError("non-array var indexing requires bytes dt")

View File

@ -58,60 +58,6 @@ fun PtExpression.isSimple(): Boolean {
}
}
internal fun PtIdentifier.targetStatement(program: PtProgram): PtNode {
return if(name in BuiltinFunctions)
this // just reuse the node itself to refer to the builtin function
else
program.lookup(name)
}
internal fun PtProgram.lookup(name: String): PtNode {
// TODO should be cached?
fun searchLocalSymbol(node: PtNode, namePart: String): PtNode? {
when(node) {
is PtProgram -> {
return node.allBlocks().single { it.name==namePart }
}
is PtNamedNode -> {
if(node.name==namePart)
return node
}
is PtNodeGroup -> {
node.children.forEach {
val found = searchLocalSymbol(it, namePart)
if(found!=null)
return found
}
}
is PtIdentifier -> {
if(node.name==namePart)
return node
}
is PtSubroutineParameter -> {
if(node.name==namePart)
return node
}
else -> {
// NOTE: when other nodes containing scopes are introduced,
// these should be added here as well to look into!
}
}
node.children.forEach {
val found = searchLocalSymbol(it, namePart)
if(found!=null)
return found
}
return null
}
val remainder = name.splitToSequence('.')
return remainder.fold(this as PtNode) { acc, namePart -> searchLocalSymbol(acc, namePart)!! }
}
internal fun PtIdentifier.targetVarDecl(program: PtProgram): IPtVariable? =
this.targetStatement(program) as? IPtVariable
internal fun IPtSubroutine.regXasResult(): Boolean =
(this is PtAsmSub) && this.retvalRegisters.any { it.registerOrPair in arrayOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
@ -132,16 +78,6 @@ internal fun PtAsmSub.shouldKeepA(): KeepAresult {
return KeepAresult(false, saveAonReturn)
}
internal fun PtFunctionCall.targetSubroutine(program: PtProgram): IPtSubroutine =
this.targetStatement(program) as IPtSubroutine
internal fun PtFunctionCall.targetStatement(program: PtProgram): PtNode {
return if(name in BuiltinFunctions)
this // just reuse the node itself to refer to the builtin function
else
program.lookup(name)
}
internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<DataType, RegisterOrStatusflag>> {
when(this) {
is PtAsmSub -> {
@ -196,4 +132,12 @@ internal fun PtSub.returnRegister(): RegisterOrStatusflag? {
null -> null
else -> RegisterOrStatusflag(RegisterOrPair.AY, null)
}
}
}
// TODO move into AsmGen:
internal fun findSubroutineParameter(name: String, asmgen: AsmGen): PtSubroutineParameter? {
val node = asmgen.symbolTable.lookup(name)!!.astNode
if(node is PtSubroutineParameter)
return node
return node.definingSub()?.parameters?.singleOrNull { it.name===name }
}

View File

@ -239,7 +239,8 @@ $endLabel""")
val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel)
val iterableName = asmgen.asmVariableName(ident)
val decl = ident.targetVarDecl(program)!! as PtVariable
val symbol = asmgen.symbolTable.lookup(ident.name)
val decl = symbol!!.astNode as PtVariable
when(iterableDt) {
DataType.STR -> {
asmgen.out("""

View File

@ -18,7 +18,8 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
}
internal fun saveXbeforeCall(stmt: PtFunctionCall) {
val sub = stmt.targetSubroutine(program)
val symbol = asmgen.symbolTable.lookup(stmt.name)
val sub = symbol!!.astNode as IPtSubroutine
if(sub.shouldSaveX()) {
if(sub is PtAsmSub) {
val regSaveOnStack = sub.address == null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
@ -32,7 +33,8 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
}
internal fun restoreXafterCall(stmt: PtFunctionCall) {
val sub = stmt.targetSubroutine(program)
val symbol = asmgen.symbolTable.lookup(stmt.name)
val sub = symbol!!.astNode as IPtSubroutine
if(sub.shouldSaveX()) {
if(sub is PtAsmSub) {
val regSaveOnStack = sub.address == null // rom-routines don't require registers to be saved on stack, normal subroutines do because they can contain nested calls
@ -55,7 +57,8 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
// NOTE: does NOT output code to save/restore the X register for this call! Every caller should deal with this in their own way!!
// (you can use subroutine.shouldSaveX() and saveX()/restoreX() routines as a help for this)
val sub: IPtSubroutine = call.targetSubroutine(program)
val symbol = asmgen.symbolTable.lookup(call.name)
val sub = symbol!!.astNode as IPtSubroutine
val subAsmName = asmgen.asmSymbolName(call.name)
if(sub is PtAsmSub) {

View File

@ -2,7 +2,10 @@ package prog8.codegen.cpu6502.assignment
import prog8.code.ast.*
import prog8.code.core.*
import prog8.codegen.cpu6502.*
import prog8.codegen.cpu6502.AsmGen
import prog8.codegen.cpu6502.asConstInteger
import prog8.codegen.cpu6502.findSubroutineParameter
import prog8.codegen.cpu6502.returnsWhatWhere
internal enum class TargetStorageKind {
@ -54,8 +57,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
with(assign.target) {
when {
identifier != null -> {
val paramName = identifier!!.targetVarDecl(program)?.name
val parameter = identifier!!.targetStatement(program).definingSub()?.parameters?.singleOrNull { it.name===paramName }
val parameter = findSubroutineParameter(identifier!!.name, asmgen)
if (parameter!=null) {
val sub = parameter.definingAsmSub()
if (sub!=null) {
@ -134,8 +136,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
is PtString -> throw AssemblyError("string literal value should not occur anymore for asm generation")
is PtArray -> throw AssemblyError("array literal value should not occur anymore for asm generation")
is PtIdentifier -> {
val paramName = value.targetVarDecl(program)?.name
val parameter = value.targetStatement(program).definingSub()?.parameters?.singleOrNull { it.name===paramName }
val parameter = findSubroutineParameter(value.name, asmgen)
if(parameter?.definingAsmSub() != null)
throw AssemblyError("can't assign from a asmsub register parameter $value ${value.position}")
val varName=asmgen.asmVariableName(value)
@ -158,7 +159,8 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, value.type, expression = value)
}
is PtFunctionCall -> {
val sub = value.targetSubroutine(program)
val symbol = asmgen.symbolTable.lookup(value.name)
val sub = symbol!!.astNode as IPtSubroutine
val returnType = sub.returnsWhatWhere().firstOrNull { rr -> rr.second.registerOrPair != null || rr.second.statusflag!=null }?.first
?: throw AssemblyError("can't translate zero return values in assignment")

View File

@ -13,7 +13,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
fun translate(assignment: PtAssignment) {
val target = AsmAssignTarget.fromAstAssignment(assignment, program, asmgen)
val source = AsmAssignSource.fromAstSource(assignment.value, program, asmgen).adjustSignedUnsigned(target)
val assign = AsmAssignment(source, target, assignment.isInplaceAssign, program.memsizer, assignment.position)
target.origAssign = assign
@ -178,7 +177,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
is PtMemoryByte -> throw AssemblyError("source kind should have been memory")
is PtTypeCast -> assignTypeCastedValue(assign.target, value.type, value.value, value)
is PtFunctionCall -> {
val sub = value.targetSubroutine(program)
val symbol = asmgen.symbolTable.lookup(value.name)
val sub = symbol!!.astNode as IPtSubroutine
asmgen.saveXbeforeCall(value)
asmgen.translateFunctionCall(value, true)
val returnValue = sub.returnsWhatWhere().singleOrNull() { it.second.registerOrPair!=null } ?: sub.returnsWhatWhere().single() { it.second.statusflag!=null }
@ -807,7 +807,8 @@ internal class AssignmentAsmGen(private val program: PtProgram,
private fun containmentCheckIntoA(containment: PtContainmentCheck) {
val elementDt = containment.element.type
val variable = (containment.iterable as? PtIdentifier)?.targetVarDecl(program) as PtVariable
val symbol = asmgen.symbolTable.lookup(containment.iterable.name)
val variable = symbol!!.astNode as PtVariable
val varname = asmgen.asmVariableName(containment.iterable)
when(variable.type) {
DataType.STR -> {

View File

@ -11,7 +11,7 @@ import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.Directive
import prog8.ast.statements.VarDecl
import prog8.ast.walk.IAstVisitor
import prog8.code.SymbolTable
import prog8.code.SymbolTableMaker
import prog8.code.core.*
import prog8.code.target.*
import prog8.codegen.vm.VmCodeGen
@ -392,7 +392,6 @@ private fun createAssemblyAndAssemble(program: Program,
compilerOptions.compTarget.machine.initializeMemoryAreas(compilerOptions)
program.processAstBeforeAsmGeneration(compilerOptions, errors)
errors.report()
val symbolTable = SymbolTableMaker(program, compilerOptions).make()
// 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.
@ -403,7 +402,7 @@ private fun createAssemblyAndAssemble(program: Program,
// println("*********** COMPILER AST RIGHT BEFORE ASM GENERATION *************")
// printProgram(program)
val assembly = asmGeneratorFor(program, errors, symbolTable, compilerOptions).compileToAssembly()
val assembly = asmGeneratorFor(program, errors, compilerOptions).compileToAssembly()
errors.report()
return if(assembly!=null && errors.noErrors()) {
@ -442,10 +441,13 @@ fun printProgram(program: Program) {
internal fun asmGeneratorFor(program: Program,
errors: IErrorReporter,
symbolTable: SymbolTable,
options: CompilationOptions): IAssemblyGenerator
{
val intermediateAst = IntermediateAstMaker(program, symbolTable, options).transform()
val intermediateAst = IntermediateAstMaker(program, options).transform()
val stMaker = SymbolTableMaker(intermediateAst, options)
val symbolTable = stMaker.make()
if(options.experimentalCodegen)
return prog8.codegen.experimental.CodeGen(intermediateAst, symbolTable, options, errors)
else if (options.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02))

View File

@ -8,7 +8,6 @@ import prog8.ast.Program
import prog8.ast.base.FatalAstException
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.code.SymbolTable
import prog8.code.ast.*
import prog8.code.core.BuiltinFunctions
import prog8.code.core.CompilationOptions
@ -23,7 +22,7 @@ import kotlin.io.path.isRegularFile
/**
* Convert 'old' compiler-AST into the 'new' simplified AST with baked types.
*/
class IntermediateAstMaker(private val program: Program, private val symbolTable: SymbolTable, private val options: CompilationOptions) {
class IntermediateAstMaker(private val program: Program, private val options: CompilationOptions) {
fun transform(): PtProgram {
val ptProgram = PtProgram(
program.name,
@ -386,8 +385,8 @@ class IntermediateAstMaker(private val program: Program, private val symbolTable
}
private fun transform(srcArr: ArrayIndexedExpression): PtArrayIndexer {
val type = srcArr.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
val array = PtArrayIndexer(type, srcArr.position)
val arrayVarType = srcArr.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
val array = PtArrayIndexer(arrayVarType, srcArr.position)
array.add(transform(srcArr.arrayvar))
array.add(transformExpression(srcArr.indexer.indexExpr))
return array

View File

@ -1,150 +0,0 @@
package prog8.compiler.astprocessing
import prog8.ast.Program
import prog8.ast.base.FatalAstException
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.ast.walk.IAstVisitor
import prog8.code.*
import prog8.code.core.ArrayDatatypes
import prog8.code.core.CompilationOptions
import prog8.code.core.DataType
import prog8.code.core.Position
import prog8.code.target.VMTarget
import java.util.*
internal class SymbolTableMaker(private val program: Program, private val options: CompilationOptions): IAstVisitor {
private val st = SymbolTable()
private val scopestack = Stack<StNode>()
private var dontReinitGlobals = false
fun make(): SymbolTable {
scopestack.clear()
st.children.clear()
dontReinitGlobals = options.dontReinitGlobals
this.visit(program)
program.builtinFunctions.names.forEach {
val node = StNode(it, StNodeType.BUILTINFUNC, Position.DUMMY)
st.add(node)
}
require(scopestack.isEmpty())
if(options.compTarget.name!=VMTarget.NAME) {
// add the hardcoded temporary zeropage variables for targets that use them
st.add(StMemVar("P8ZP_SCRATCH_B1", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_B1, null, Position.DUMMY))
st.add(StMemVar("P8ZP_SCRATCH_REG", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_REG, null, Position.DUMMY))
st.add(StMemVar("P8ZP_SCRATCH_W1", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W1, null, Position.DUMMY))
st.add(StMemVar("P8ZP_SCRATCH_W2", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W2, null, Position.DUMMY))
st.add(StMemVar("P8ESTACK_LO", DataType.UBYTE, options.compTarget.machine.ESTACK_LO, null, Position.DUMMY))
st.add(StMemVar("P8ESTACK_HI", DataType.UBYTE, options.compTarget.machine.ESTACK_HI, null, Position.DUMMY))
}
return st
}
override fun visit(block: Block) {
val node = StNode(block.name, StNodeType.BLOCK, block.position)
st.add(node)
scopestack.push(node)
super.visit(block)
scopestack.pop()
// st.origAstLinks[block] = node
}
override fun visit(subroutine: Subroutine) {
if(subroutine.asmAddress!=null) {
val parameters = subroutine.parameters.zip(subroutine.asmParameterRegisters).map { StRomSubParameter(it.second, it.first.type) }
val node = StRomSub(subroutine.name, subroutine.asmAddress!!, parameters, subroutine.asmReturnvaluesRegisters, subroutine.position)
scopestack.peek().add(node)
// st.origAstLinks[subroutine] = node
} else {
val parameters = subroutine.parameters.map { StSubroutineParameter(it.name, it.type) }
val returnType = if(subroutine.returntypes.isEmpty()) null else subroutine.returntypes.first()
val node = StSub(subroutine.name, parameters, returnType, subroutine.position)
scopestack.peek().add(node)
scopestack.push(node)
super.visit(subroutine)
scopestack.pop()
// st.origAstLinks[subroutine] = node
}
}
override fun visit(decl: VarDecl) {
val node =
when(decl.type) {
VarDeclType.VAR -> {
var initialNumeric = (decl.value as? NumericLiteral)?.number
if(initialNumeric==0.0)
initialNumeric=null // variable will go into BSS and this will be set to 0
val initialStringLit = decl.value as? StringLiteral
val initialString = if(initialStringLit==null) null else Pair(initialStringLit.value, initialStringLit.encoding)
val initialArrayLit = decl.value as? ArrayLiteral
val initialArray = makeInitialArray(initialArrayLit)
if(decl.isArray && decl.datatype !in ArrayDatatypes)
throw FatalAstException("array vardecl has mismatched dt ${decl.datatype}")
val numElements =
if(decl.isArray)
decl.arraysize!!.constIndex()
else if(initialStringLit!=null)
initialStringLit.value.length+1 // include the terminating 0-byte
else
null
val bss = if(decl.datatype==DataType.STR)
false
else if(decl.isArray)
initialArray.isNullOrEmpty()
else
initialNumeric == null
StStaticVariable(decl.name, decl.datatype, bss, initialNumeric, initialString, initialArray, numElements, decl.zeropage, decl.position)
}
VarDeclType.CONST -> StConstant(decl.name, decl.datatype, (decl.value as NumericLiteral).number, decl.position)
VarDeclType.MEMORY -> {
val numElements =
if(decl.datatype in ArrayDatatypes)
decl.arraysize!!.constIndex()
else null
StMemVar(decl.name, decl.datatype, (decl.value as NumericLiteral).number.toUInt(), numElements, decl.position)
}
}
scopestack.peek().add(node)
// st.origAstLinks[decl] = node
}
private fun makeInitialArray(arrayLit: ArrayLiteral?): StArray? {
if(arrayLit==null)
return null
return arrayLit.value.map {
when(it){
is AddressOf -> {
val scopedName = it.identifier.targetNameAndType(program).first
StArrayElement(null, scopedName)
}
is IdentifierReference -> {
val scopedName = it.targetNameAndType(program).first
StArrayElement(null, scopedName)
}
is NumericLiteral -> StArrayElement(it.number, null)
else -> throw FatalAstException("weird element dt in array literal")
}
}.toList()
}
override fun visit(label: Label) {
val node = StNode(label.name, StNodeType.LABEL, label.position)
scopestack.peek().add(node)
// st.origAstLinks[label] = node
}
override fun visit(bfc: BuiltinFunctionCall) {
if(bfc.name=="memory") {
// memory slab allocations are a builtin functioncall in the program, but end up named as well in the symboltable
val name = (bfc.args[0] as StringLiteral).value
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name"}
val size = (bfc.args[1] as NumericLiteral).number.toUInt()
val align = (bfc.args[2] as NumericLiteral).number.toUInt()
st.add(StMemorySlab("prog8_memoryslab_$name", size, align, bfc.position))
}
super.visit(bfc)
}
}

View File

@ -1,14 +1,9 @@
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.code.*
import prog8.code.core.DataType
import prog8.code.core.Position
import prog8.code.core.ZeropageWish
// TODO new unit tests for Symbol Table on new Ast Nodes
/*
class TestSymbolTable: FunSpec({
test("empty symboltable") {
val st = SymbolTable()
@ -98,4 +93,4 @@ private fun makeSt(): SymbolTable {
st.add(block2)
st.add(builtinfunc)
return st
}
}*/

View File

@ -8,7 +8,6 @@ import prog8.code.ast.*
import prog8.code.core.*
import prog8.code.target.C64Target
import prog8.compiler.astprocessing.IntermediateAstMaker
import prog8.compiler.astprocessing.SymbolTableMaker
import prog8tests.helpers.compileText
class TestIntermediateAst: FunSpec({
@ -38,8 +37,7 @@ class TestIntermediateAst: FunSpec({
loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
)
val result = compileText(target, false, text, writeAssembly = false)!!
val st = SymbolTableMaker(result.program, options).make()
val ast = IntermediateAstMaker(result.program, st, options).transform()
val ast = IntermediateAstMaker(result.program, options).transform()
ast.name shouldBe result.program.name
ast.allBlocks().any() shouldBe true
val entry = ast.entrypoint() ?: fail("no main.start() found")

View File

@ -56,8 +56,7 @@ main {
compileText(VMTarget(), false, text, writeAssembly = true) shouldNotBe null
}
// TODO implement this in 6502 codegen and re-enable test
xtest("array in-place negation (float type) 6502 target") {
test("array in-place negation (float type) 6502 target") {
val text = """
%import floats

View File

@ -9,6 +9,7 @@ import prog8.ast.expressions.AddressOf
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.*
import prog8.code.SymbolTableMaker
import prog8.code.ast.PtAddressOf
import prog8.code.ast.PtAssignment
import prog8.code.ast.PtIdentifier
@ -17,7 +18,6 @@ import prog8.code.target.C64Target
import prog8.code.target.VMTarget
import prog8.codegen.cpu6502.AsmGen
import prog8.compiler.astprocessing.IntermediateAstMaker
import prog8.compiler.astprocessing.SymbolTableMaker
import prog8tests.helpers.*
class TestAsmGenSymbols: StringSpec({
@ -74,8 +74,8 @@ class TestAsmGenSymbols: StringSpec({
fun createTestAsmGen(program: Program): AsmGen {
val errors = ErrorReporterForTests()
val options = CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), false, true, C64Target(), 999u)
val st = SymbolTableMaker(program, options).make()
val ptProgram = IntermediateAstMaker(program, st, options).transform()
val ptProgram = IntermediateAstMaker(program, options).transform()
val st = SymbolTableMaker(ptProgram, options).make()
return AsmGen(ptProgram, st, options, errors)
}

View File

@ -21,4 +21,41 @@ main {
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
}
test("nested scoping") {
val text="""
main {
sub start() {
testscope.duplicate()
cx16.r0L = testscope.duplicate2()
}
}
testscope {
sub sub1() {
ubyte @shared duplicate
ubyte @shared duplicate2
}
sub duplicate() {
; do nothing
}
sub duplicate2() -> ubyte {
return cx16.r0L
}
}"""
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
}
test("word array indexing") {
val text="""
main {
sub start() {
uword[3] seed
cx16.r0 = seed[0] + seed[1] + seed[2]
}
}"""
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
}
})

View File

@ -1,6 +1,9 @@
main {
sub start() {
cx16.r0 = memory("slab", $c000, 0)
cx16.r1 = memory("slab", $c000, 0)
testscope.duplicate()
cx16.r0L = testscope.duplicate2()
}

View File

@ -1,6 +1,7 @@
package prog8.intermediate
import prog8.code.*
import prog8.code.ast.PtVariable
import prog8.code.core.*
import prog8.code.target.*
import java.io.StringReader
@ -170,7 +171,9 @@ class IRFileReader {
val arraysize = if(arrayspec.isNotBlank()) arrayspec.substring(1, arrayspec.length-1).toInt() else null
val dt: DataType = parseDatatype(type, arraysize!=null)
val zp = if(zpwish.isBlank()) ZeropageWish.DONTCARE else ZeropageWish.valueOf(zpwish)
bssVariables.add(StStaticVariable(name, dt, true, null, null, null, arraysize, zp, Position.DUMMY))
val dummyNode = PtVariable(name, dt, null, null, Position.DUMMY)
val newVar = StStaticVariable(name, dt, true, null, null, null, arraysize, zp, dummyNode, Position.DUMMY)
bssVariables.add(newVar)
}
return bssVariables
}
@ -231,7 +234,8 @@ class IRFileReader {
else -> throw IRParseException("weird dt")
}
require(!bss) { "bss var should be in BSS section" }
variables.add(StStaticVariable(name, dt, bss, initNumeric, null, initArray, arraysize, zp, Position.DUMMY))
val dummyNode = PtVariable(name, dt, null, null, Position.DUMMY)
variables.add(StStaticVariable(name, dt, bss, initNumeric, null, initArray, arraysize, zp, dummyNode, Position.DUMMY))
}
return variables
}
@ -257,7 +261,8 @@ class IRFileReader {
val (type, arrayspec, name, address) = match.destructured
val arraysize = if(arrayspec.isNotBlank()) arrayspec.substring(1, arrayspec.length-1).toInt() else null
val dt: DataType = parseDatatype(type, arraysize!=null)
memvars.add(StMemVar(name, dt, parseIRValue(address).toUInt(), arraysize, Position.DUMMY))
val dummyNode = PtVariable(name, dt, null, null, Position.DUMMY)
memvars.add(StMemVar(name, dt, parseIRValue(address).toUInt(), arraysize, dummyNode, Position.DUMMY))
}
memvars
}
@ -279,7 +284,8 @@ class IRFileReader {
// example: "SLAB slabname 4096 0"
val match = slabPattern.matchEntire(line) ?: throw IRParseException("invalid SLAB $line")
val (name, size, align) = match.destructured
slabs.add(StMemorySlab(name, size.toUInt(), align.toUInt(), Position.DUMMY))
val dummyNode = PtVariable(name, DataType.ARRAY_UB, null, null, Position.DUMMY)
slabs.add(StMemorySlab(name, size.toUInt(), align.toUInt(), dummyNode, Position.DUMMY))
}
slabs
}

View File

@ -1,6 +1,8 @@
package prog8.intermediate
import prog8.code.*
import prog8.code.ast.PtVariable
import prog8.code.core.DataType
// In the Intermediate Representation, all nesting has been removed.
@ -71,12 +73,14 @@ class IRSymbolTable(sourceSt: SymbolTable?) {
return newArray
}
scopedName = variable.scopedName
val dummyNode = PtVariable(scopedName, variable.dt, null, null, variable.position)
varToadd = StStaticVariable(scopedName, variable.dt, variable.bss,
variable.onetimeInitializationNumericValue,
variable.onetimeInitializationStringValue,
fixupAddressOfInArray(variable.onetimeInitializationArrayValue),
variable.length,
variable.zpwish,
dummyNode,
variable.position
)
}
@ -92,7 +96,8 @@ class IRSymbolTable(sourceSt: SymbolTable?) {
varToadd = variable
} else {
scopedName = variable.scopedName
varToadd = StMemVar(scopedName, variable.dt, variable.address, variable.length, variable.position)
val dummyNode = PtVariable(scopedName, variable.dt, null, null, variable.position)
varToadd = StMemVar(scopedName, variable.dt, variable.address, variable.length, dummyNode, variable.position)
}
table[scopedName] = varToadd
}
@ -100,8 +105,10 @@ class IRSymbolTable(sourceSt: SymbolTable?) {
fun add(variable: StMemorySlab) {
val varToadd = if('.' in variable.name)
variable
else
StMemorySlab("prog8_slabs.${variable.name}", variable.size, variable.align, variable.position)
else {
val dummyNode = PtVariable(variable.name, DataType.ARRAY_UB, null, null, variable.position)
StMemorySlab("prog8_slabs.${variable.name}", variable.size, variable.align, dummyNode, variable.position)
}
table[varToadd.name] = varToadd
}