mirror of
https://github.com/irmen/prog8.git
synced 2024-11-22 15:33:02 +00:00
lookup via new ST
This commit is contained in:
parent
109e118aba
commit
c75b1581d2
@ -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)
|
||||
|
212
codeCore/src/prog8/code/SymbolTableMaker.kt
Normal file
212
codeCore/src/prog8/code/SymbolTableMaker.kt
Normal 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()
|
||||
// }
|
||||
//
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
@ -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()}")
|
||||
|
@ -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 -> {
|
||||
|
@ -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!!
|
||||
|
@ -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")
|
||||
|
@ -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 }
|
||||
}
|
||||
|
@ -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("""
|
||||
|
@ -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) {
|
||||
|
@ -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")
|
||||
|
||||
|
@ -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 -> {
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}*/
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
})
|
@ -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()
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user