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 package prog8.code
import prog8.code.ast.PtNode
import prog8.code.core.* import prog8.code.core.*
@ -7,7 +8,7 @@ import prog8.code.core.*
* Tree structure containing all symbol definitions in the program * Tree structure containing all symbol definitions in the program
* (blocks, subroutines, variables (all types), memoryslabs, and labels). * (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. * The table as a flat mapping of scoped names to the StNode.
* This gives the fastest lookup possible (no need to traverse tree nodes) * 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, open class StNode(val name: String,
val type: StNodeType, val type: StNodeType,
val position: Position, val position: Position,
val astNode: PtNode, // TODO keep reference to the node in the AST
val children: MutableMap<String, StNode> = mutableMapOf() val children: MutableMap<String, StNode> = mutableMapOf()
) { ) {
@ -151,7 +153,8 @@ class StStaticVariable(name: String,
val onetimeInitializationArrayValue: StArray?, val onetimeInitializationArrayValue: StArray?,
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
val zpwish: ZeropageWish, val zpwish: ZeropageWish,
position: Position) : StNode(name, StNodeType.STATICVAR, position) { astNode: PtNode,
position: Position) : StNode(name, StNodeType.STATICVAR, position, astNode = astNode) {
init { init {
if(bss) { if(bss) {
@ -180,8 +183,8 @@ class StStaticVariable(name: String,
} }
class StConstant(name: String, val dt: DataType, val value: Double, position: Position) : class StConstant(name: String, val dt: DataType, val value: Double, astNode: PtNode, position: Position) :
StNode(name, StNodeType.CONSTANT, position) { StNode(name, StNodeType.CONSTANT, position, astNode) {
} }
@ -189,21 +192,23 @@ class StMemVar(name: String,
val dt: DataType, val dt: DataType,
val address: UInt, val address: UInt,
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
astNode: PtNode,
position: Position) : position: Position) :
StNode(name, StNodeType.MEMVAR, position) { StNode(name, StNodeType.MEMVAR, position, astNode) {
} }
class StMemorySlab( class StMemorySlab(
name: String, name: String,
val size: UInt, val size: UInt,
val align: UInt, val align: UInt,
astNode: PtNode,
position: Position 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) : class StSub(name: String, val parameters: List<StSubroutineParameter>, val returnType: DataType?, astNode: PtNode, position: Position) :
StNode(name, StNodeType.SUBROUTINE, position) { StNode(name, StNodeType.SUBROUTINE, position, astNode) {
} }
@ -211,9 +216,10 @@ class StRomSub(name: String,
val address: UInt, val address: UInt,
val parameters: List<StRomSubParameter>, val parameters: List<StRomSubParameter>,
val returns: List<RegisterOrStatusflag>, val returns: List<RegisterOrStatusflag>,
astNode: PtNode,
position: Position) : position: Position) :
StNode(name, StNodeType.ROMSUB, position) { StNode(name, StNodeType.ROMSUB, position, astNode)
}
class StSubroutineParameter(val name: String, val type: DataType) 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 package prog8.code.ast
import prog8.code.core.DataType import prog8.code.core.*
import prog8.code.core.Encoding
import prog8.code.core.Position
import java.util.* import java.util.*
import kotlin.math.round 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 val variable: PtIdentifier
get() = children[0] as PtIdentifier get() = children[0] as PtIdentifier
val index: PtExpression val index: PtExpression
get() = children[1] as 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 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 { class PtMemMapped(name: String, override val type: DataType, val address: UInt, position: Position) : PtNamedNode(name, position), IPtVariable {
override fun printProperties() { override fun printProperties() {
print("&$type $name = ${address.toHex()}") print("&$type $name = ${address.toHex()}")

View File

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

View File

@ -52,15 +52,17 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
require(fcall.args[0] is PtIdentifier) { 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}" "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) val symbol = asmgen.symbolTable.lookup((fcall.args[0] as PtIdentifier).name)
asmgen.popCpuStack(DataType.UBYTE, target!!, fcall.definingISub()) val target = symbol!!.astNode as IPtVariable
asmgen.popCpuStack(DataType.UBYTE, target, fcall.definingISub())
} }
"popw" -> { "popw" -> {
require(fcall.args[0] is PtIdentifier) { 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}" "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) val symbol = asmgen.symbolTable.lookup((fcall.args[0] as PtIdentifier).name)
asmgen.popCpuStack(DataType.UWORD, target!!, fcall.definingISub()) val target = symbol!!.astNode as IPtVariable
asmgen.popCpuStack(DataType.UWORD, target, fcall.definingISub())
} }
"rsave" -> funcRsave() "rsave" -> funcRsave()
"rsavex" -> funcRsaveX() "rsavex" -> funcRsaveX()
@ -349,7 +351,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcReverse(fcall: PtBuiltinFunctionCall) { private fun funcReverse(fcall: PtBuiltinFunctionCall) {
val variable = fcall.args.single() val variable = fcall.args.single()
if (variable is PtIdentifier) { 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 varName = asmgen.asmVariableName(variable)
val numElements = decl.arraySize!! val numElements = decl.arraySize!!
when (decl.type) { when (decl.type) {
@ -388,7 +391,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcSort(fcall: PtBuiltinFunctionCall) { private fun funcSort(fcall: PtBuiltinFunctionCall) {
val variable = fcall.args.single() val variable = fcall.args.single()
if (variable is PtIdentifier) { 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 varName = asmgen.asmVariableName(variable)
val numElements = decl.arraySize!! val numElements = decl.arraySize!!
when (decl.type) { when (decl.type) {
@ -1007,7 +1011,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun outputAddressAndLenghtOfArray(arg: PtExpression) { private fun outputAddressAndLenghtOfArray(arg: PtExpression) {
// address in P8ZP_SCRATCH_W1, number of elements in A // address in P8ZP_SCRATCH_W1, number of elements in A
arg as PtIdentifier 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) if(arrayVar.arraySize==null)
throw AssemblyError("length of non-array requested") throw AssemblyError("length of non-array requested")
val size = arrayVar.arraySize!! val size = arrayVar.arraySize!!

View File

@ -44,7 +44,8 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
private fun translateFunctionCallResultOntoStack(call: PtFunctionCall) { private fun translateFunctionCallResultOntoStack(call: PtFunctionCall) {
// only for use in nested expression evaluation // 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.saveXbeforeCall(call)
asmgen.translateFunctionCall(call, true) asmgen.translateFunctionCall(call, true)
if(sub.regXasResult()) { if(sub.regXasResult()) {
@ -725,7 +726,7 @@ internal class ExpressionsAsmGen(private val program: PtProgram,
val elementDt = arrayExpr.type val elementDt = arrayExpr.type
val arrayVarName = asmgen.asmVariableName(arrayExpr.variable) 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 // indexing a pointer var instead of a real array or string
if(elementDt !in ByteDatatypes) if(elementDt !in ByteDatatypes)
throw AssemblyError("non-array var indexing requires bytes dt") 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 = internal fun IPtSubroutine.regXasResult(): Boolean =
(this is PtAsmSub) && this.retvalRegisters.any { it.registerOrPair in arrayOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) } (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) 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>> { internal fun IPtSubroutine.returnsWhatWhere(): List<Pair<DataType, RegisterOrStatusflag>> {
when(this) { when(this) {
is PtAsmSub -> { is PtAsmSub -> {
@ -196,4 +132,12 @@ internal fun PtSub.returnRegister(): RegisterOrStatusflag? {
null -> null null -> null
else -> RegisterOrStatusflag(RegisterOrPair.AY, 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") val endLabel = asmgen.makeLabel("for_end")
asmgen.loopEndLabels.push(endLabel) asmgen.loopEndLabels.push(endLabel)
val iterableName = asmgen.asmVariableName(ident) 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) { when(iterableDt) {
DataType.STR -> { DataType.STR -> {
asmgen.out(""" asmgen.out("""

View File

@ -18,7 +18,8 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
} }
internal fun saveXbeforeCall(stmt: PtFunctionCall) { 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.shouldSaveX()) {
if(sub is PtAsmSub) { 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 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) { 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.shouldSaveX()) {
if(sub is PtAsmSub) { 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 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!! // 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) // (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) val subAsmName = asmgen.asmSymbolName(call.name)
if(sub is PtAsmSub) { if(sub is PtAsmSub) {

View File

@ -2,7 +2,10 @@ package prog8.codegen.cpu6502.assignment
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.* 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 { internal enum class TargetStorageKind {
@ -54,8 +57,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
with(assign.target) { with(assign.target) {
when { when {
identifier != null -> { identifier != null -> {
val paramName = identifier!!.targetVarDecl(program)?.name val parameter = findSubroutineParameter(identifier!!.name, asmgen)
val parameter = identifier!!.targetStatement(program).definingSub()?.parameters?.singleOrNull { it.name===paramName }
if (parameter!=null) { if (parameter!=null) {
val sub = parameter.definingAsmSub() val sub = parameter.definingAsmSub()
if (sub!=null) { 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 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 PtArray -> throw AssemblyError("array literal value should not occur anymore for asm generation")
is PtIdentifier -> { is PtIdentifier -> {
val paramName = value.targetVarDecl(program)?.name val parameter = findSubroutineParameter(value.name, asmgen)
val parameter = value.targetStatement(program).definingSub()?.parameters?.singleOrNull { it.name===paramName }
if(parameter?.definingAsmSub() != null) if(parameter?.definingAsmSub() != null)
throw AssemblyError("can't assign from a asmsub register parameter $value ${value.position}") throw AssemblyError("can't assign from a asmsub register parameter $value ${value.position}")
val varName=asmgen.asmVariableName(value) val varName=asmgen.asmVariableName(value)
@ -158,7 +159,8 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, value.type, expression = value) AsmAssignSource(SourceStorageKind.EXPRESSION, program, asmgen, value.type, expression = value)
} }
is PtFunctionCall -> { 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 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") ?: 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) { fun translate(assignment: PtAssignment) {
val target = AsmAssignTarget.fromAstAssignment(assignment, program, asmgen) val target = AsmAssignTarget.fromAstAssignment(assignment, program, asmgen)
val source = AsmAssignSource.fromAstSource(assignment.value, program, asmgen).adjustSignedUnsigned(target) val source = AsmAssignSource.fromAstSource(assignment.value, program, asmgen).adjustSignedUnsigned(target)
val assign = AsmAssignment(source, target, assignment.isInplaceAssign, program.memsizer, assignment.position) val assign = AsmAssignment(source, target, assignment.isInplaceAssign, program.memsizer, assignment.position)
target.origAssign = assign 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 PtMemoryByte -> throw AssemblyError("source kind should have been memory")
is PtTypeCast -> assignTypeCastedValue(assign.target, value.type, value.value, value) is PtTypeCast -> assignTypeCastedValue(assign.target, value.type, value.value, value)
is PtFunctionCall -> { is PtFunctionCall -> {
val sub = value.targetSubroutine(program) val symbol = asmgen.symbolTable.lookup(value.name)
val sub = symbol!!.astNode as IPtSubroutine
asmgen.saveXbeforeCall(value) asmgen.saveXbeforeCall(value)
asmgen.translateFunctionCall(value, true) asmgen.translateFunctionCall(value, true)
val returnValue = sub.returnsWhatWhere().singleOrNull() { it.second.registerOrPair!=null } ?: sub.returnsWhatWhere().single() { it.second.statusflag!=null } 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) { private fun containmentCheckIntoA(containment: PtContainmentCheck) {
val elementDt = containment.element.type 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) val varname = asmgen.asmVariableName(containment.iterable)
when(variable.type) { when(variable.type) {
DataType.STR -> { DataType.STR -> {

View File

@ -11,7 +11,7 @@ import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.Directive import prog8.ast.statements.Directive
import prog8.ast.statements.VarDecl import prog8.ast.statements.VarDecl
import prog8.ast.walk.IAstVisitor import prog8.ast.walk.IAstVisitor
import prog8.code.SymbolTable import prog8.code.SymbolTableMaker
import prog8.code.core.* import prog8.code.core.*
import prog8.code.target.* import prog8.code.target.*
import prog8.codegen.vm.VmCodeGen import prog8.codegen.vm.VmCodeGen
@ -392,7 +392,6 @@ private fun createAssemblyAndAssemble(program: Program,
compilerOptions.compTarget.machine.initializeMemoryAreas(compilerOptions) compilerOptions.compTarget.machine.initializeMemoryAreas(compilerOptions)
program.processAstBeforeAsmGeneration(compilerOptions, errors) program.processAstBeforeAsmGeneration(compilerOptions, errors)
errors.report() 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, // 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.
@ -403,7 +402,7 @@ private fun createAssemblyAndAssemble(program: Program,
// println("*********** COMPILER AST RIGHT BEFORE ASM GENERATION *************") // println("*********** COMPILER AST RIGHT BEFORE ASM GENERATION *************")
// printProgram(program) // printProgram(program)
val assembly = asmGeneratorFor(program, errors, symbolTable, compilerOptions).compileToAssembly() val assembly = asmGeneratorFor(program, errors, compilerOptions).compileToAssembly()
errors.report() errors.report()
return if(assembly!=null && errors.noErrors()) { return if(assembly!=null && errors.noErrors()) {
@ -442,10 +441,13 @@ fun printProgram(program: Program) {
internal fun asmGeneratorFor(program: Program, internal fun asmGeneratorFor(program: Program,
errors: IErrorReporter, errors: IErrorReporter,
symbolTable: SymbolTable,
options: CompilationOptions): IAssemblyGenerator 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) if(options.experimentalCodegen)
return prog8.codegen.experimental.CodeGen(intermediateAst, symbolTable, options, errors) return prog8.codegen.experimental.CodeGen(intermediateAst, symbolTable, options, errors)
else if (options.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02)) 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.base.FatalAstException
import prog8.ast.expressions.* import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.code.SymbolTable
import prog8.code.ast.* import prog8.code.ast.*
import prog8.code.core.BuiltinFunctions import prog8.code.core.BuiltinFunctions
import prog8.code.core.CompilationOptions 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. * 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 { fun transform(): PtProgram {
val ptProgram = PtProgram( val ptProgram = PtProgram(
program.name, program.name,
@ -386,8 +385,8 @@ class IntermediateAstMaker(private val program: Program, private val symbolTable
} }
private fun transform(srcArr: ArrayIndexedExpression): PtArrayIndexer { private fun transform(srcArr: ArrayIndexedExpression): PtArrayIndexer {
val type = srcArr.inferType(program).getOrElse { throw FatalAstException("unknown dt") } val arrayVarType = srcArr.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
val array = PtArrayIndexer(type, srcArr.position) val array = PtArrayIndexer(arrayVarType, srcArr.position)
array.add(transform(srcArr.arrayvar)) array.add(transform(srcArr.arrayvar))
array.add(transformExpression(srcArr.indexer.indexExpr)) array.add(transformExpression(srcArr.indexer.indexExpr))
return array 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 package prog8tests
import io.kotest.assertions.fail // TODO new unit tests for Symbol Table on new Ast Nodes
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
/*
class TestSymbolTable: FunSpec({ class TestSymbolTable: FunSpec({
test("empty symboltable") { test("empty symboltable") {
val st = SymbolTable() val st = SymbolTable()
@ -98,4 +93,4 @@ private fun makeSt(): SymbolTable {
st.add(block2) st.add(block2)
st.add(builtinfunc) st.add(builtinfunc)
return st return st
} }*/

View File

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

View File

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

View File

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

View File

@ -21,4 +21,41 @@ main {
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null 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 { main {
sub start() { sub start() {
cx16.r0 = memory("slab", $c000, 0)
cx16.r1 = memory("slab", $c000, 0)
testscope.duplicate() testscope.duplicate()
cx16.r0L = testscope.duplicate2() cx16.r0L = testscope.duplicate2()
} }

View File

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

View File

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