changed the data type system to composite types

This commit is contained in:
Irmen de Jong 2024-12-05 21:48:51 +01:00
parent ba8c3d14f7
commit 1a1ab0dac6
99 changed files with 3466 additions and 3084 deletions

View File

@ -208,25 +208,25 @@ class StStaticVariable(name: String,
require(initializationArrayValue.isEmpty() ||initializationArrayValue.size==length)
}
if(initializationNumericValue!=null) {
require(dt in NumericDatatypes || dt==DataType.BOOL)
require(dt.isNumericOrBool)
}
if(initializationArrayValue!=null) {
require(dt in ArrayDatatypes)
require(length==initializationArrayValue.size)
require(dt.isArray)
require(length == initializationArrayValue.size)
}
if(initializationStringValue!=null) {
require(dt == DataType.STR)
require(length == initializationStringValue.first.length+1)
require(dt.isString)
require(length == initializationStringValue.first.length + 1)
}
if(align > 0) {
require(dt == DataType.STR || dt in ArrayDatatypes)
require(dt.isString || dt.isArray)
require(zpwish != ZeropageWish.REQUIRE_ZEROPAGE && zpwish != ZeropageWish.PREFER_ZEROPAGE)
}
}
}
class StConstant(name: String, val dt: DataType, val value: Double, astNode: PtNode) :
class StConstant(name: String, val dt: BaseDataType, val value: Double, astNode: PtNode) :
StNode(name, StNodeType.CONSTANT, astNode)
@ -238,8 +238,8 @@ class StMemVar(name: String,
StNode(name, StNodeType.MEMVAR, astNode) {
init{
require(dt!=DataType.BOOL && dt!=DataType.ARRAY_BOOL)
if(dt in ArrayDatatypes || dt == DataType.STR)
require(!dt.isBool && !dt.isBoolArray)
if(dt.isStringly && !dt.isWord)
requireNotNull(length)
}
}

View File

@ -10,7 +10,8 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
val st = SymbolTable(program)
BuiltinFunctions.forEach {
st.add(StNode(it.key, StNodeType.BUILTINFUNC, PtIdentifier(it.key, it.value.returnType ?: DataType.UNDEFINED, Position.DUMMY)))
val dt = DataType.forDt(it.value.returnType ?: BaseDataType.UNDEFINED)
st.add(StNode(it.key, StNodeType.BUILTINFUNC, PtIdentifier(it.key, dt, Position.DUMMY)))
}
val scopestack = ArrayDeque<StNode>()
@ -22,10 +23,10 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
if(options.compTarget.name != VMTarget.NAME) {
listOf(
PtMemMapped("P8ZP_SCRATCH_B1", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_B1, null, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_REG", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_REG, null, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_W1", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W1, null, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_W2", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W2, null, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_B1", DataType.forDt(BaseDataType.UBYTE), options.compTarget.machine.zeropage.SCRATCH_B1, null, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_REG", DataType.forDt(BaseDataType.UBYTE), options.compTarget.machine.zeropage.SCRATCH_REG, null, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_W1", DataType.forDt(BaseDataType.UWORD), options.compTarget.machine.zeropage.SCRATCH_W1, null, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_W2", DataType.forDt(BaseDataType.UWORD), options.compTarget.machine.zeropage.SCRATCH_W2, null, Position.DUMMY),
).forEach {
it.parent = program
st.add(StMemVar(it.name, it.type, it.address, it.arraySize?.toInt(), it))
@ -46,7 +47,8 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
StNode(node.name, StNodeType.BLOCK, node)
}
is PtConstant -> {
StConstant(node.name, node.type, node.value, node)
require(node.type.isNumericOrBool)
StConstant(node.name, node.type.base, node.value, node)
}
is PtLabel -> {
StNode(node.name, StNodeType.LABEL, node)

View File

@ -1,7 +1,7 @@
package prog8.code.ast
import prog8.code.core.*
import java.util.Objects
import java.util.*
import kotlin.math.abs
import kotlin.math.truncate
@ -9,7 +9,7 @@ import kotlin.math.truncate
sealed class PtExpression(val type: DataType, position: Position) : PtNode(position) {
init {
if(type==DataType.UNDEFINED) {
if(type.isUndefined) {
@Suppress("LeakingThis")
when(this) {
is PtBuiltinFunctionCall -> { /* void function call */ }
@ -66,19 +66,17 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
}
}
infix fun isSameAs(target: PtAssignTarget): Boolean {
return when {
target.memory != null && this is PtMemoryByte-> {
target.memory!!.address isSameAs this.address
}
target.identifier != null && this is PtIdentifier -> {
this.name == target.identifier!!.name
}
target.array != null && this is PtArrayIndexer -> {
this.variable.name == target.array!!.variable.name && this.index isSameAs target.array!!.index && this.splitWords==target.array!!.splitWords
}
else -> false
infix fun isSameAs(target: PtAssignTarget): Boolean = when {
target.memory != null && this is PtMemoryByte -> {
target.memory!!.address isSameAs this.address
}
target.identifier != null && this is PtIdentifier -> {
this.name == target.identifier!!.name
}
target.array != null && this is PtArrayIndexer -> {
this.variable.name == target.array!!.variable.name && this.index isSameAs target.array!!.index && this.splitWords==target.array!!.splitWords
}
else -> false
}
fun asConstInteger(): Int? = (this as? PtNumber)?.number?.toInt() ?: (this as? PtBool)?.asInt()
@ -139,7 +137,7 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
*/
}
class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) {
class PtAddressOf(position: Position) : PtExpression(DataType.forDt(BaseDataType.UWORD), position) {
val identifier: PtIdentifier
get() = children[0] as PtIdentifier
val arrayIndexExpr: PtExpression?
@ -156,10 +154,10 @@ class PtArrayIndexer(elementType: DataType, position: Position): PtExpression(el
val index: PtExpression
get() = children[1] as PtExpression
val splitWords: Boolean
get() = variable.type in SplitWordArrayTypes
get() = variable.type.isSplitWordArray
init {
require(elementType in NumericDatatypesWithBoolean)
require(elementType.isNumericOrBool)
}
}
@ -185,7 +183,7 @@ class PtBuiltinFunctionCall(val name: String,
position: Position) : PtExpression(type, position) {
init {
if(!void)
require(type!=DataType.UNDEFINED)
require(!type.isUndefined)
}
val args: List<PtExpression>
@ -201,9 +199,9 @@ class PtBinaryExpression(val operator: String, type: DataType, position: Positio
init {
if(operator in ComparisonOperators + LogicalOperators)
require(type==DataType.BOOL)
require(type.isBool)
else
require(type!=DataType.BOOL) { "no bool allowed for this operator $operator"}
require(!type.isBool) { "no bool allowed for this operator $operator"}
}
}
@ -218,7 +216,7 @@ class PtIfExpression(type: DataType, position: Position): PtExpression(type, pos
}
class PtContainmentCheck(position: Position): PtExpression(DataType.BOOL, position) {
class PtContainmentCheck(position: Position): PtExpression(DataType.forDt(BaseDataType.BOOL), position) {
val needle: PtExpression
get() = children[0] as PtExpression
val haystackHeapVar: PtIdentifier?
@ -241,7 +239,7 @@ class PtFunctionCall(val name: String,
get() = children.map { it as PtExpression }
init {
if(void) require(type==DataType.UNDEFINED) {
if(void) require(type.isUndefined) {
"void fcall should have undefined datatype"
}
// note: non-void calls can have UNDEFINED type: is if they return more than 1 value
@ -258,13 +256,13 @@ class PtIdentifier(val name: String, type: DataType, position: Position) : PtExp
}
class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position) {
class PtMemoryByte(position: Position) : PtExpression(DataType.forDt(BaseDataType.UBYTE), position) {
val address: PtExpression
get() = children.single() as PtExpression
}
class PtBool(val value: Boolean, position: Position) : PtExpression(DataType.BOOL, position) {
class PtBool(val value: Boolean, position: Position) : PtExpression(DataType.forDt(BaseDataType.BOOL), position) {
override fun hashCode(): Int = Objects.hash(type, value)
override fun equals(other: Any?): Boolean {
@ -279,28 +277,28 @@ class PtBool(val value: Boolean, position: Position) : PtExpression(DataType.BOO
}
class PtNumber(type: DataType, val number: Double, position: Position) : PtExpression(type, position) {
class PtNumber(type: BaseDataType, val number: Double, position: Position) : PtExpression(DataType.forDt(type), position) {
companion object {
fun fromBoolean(bool: Boolean, position: Position): PtNumber =
PtNumber(DataType.UBYTE, if(bool) 1.0 else 0.0, position)
PtNumber(BaseDataType.UBYTE, if(bool) 1.0 else 0.0, position)
}
init {
if(type==DataType.BOOL)
if(type==BaseDataType.BOOL)
throw IllegalArgumentException("use PtBool instead")
if(type!=DataType.FLOAT) {
if(type!=BaseDataType.FLOAT) {
val trunc = truncate(number)
if (trunc != number)
throw IllegalArgumentException("refused truncating of float to avoid loss of precision @$position")
}
when(type) {
DataType.UBYTE -> require(number in 0.0..255.0)
DataType.BYTE -> require(number in -128.0..127.0)
DataType.UWORD -> require(number in 0.0..65535.0)
DataType.WORD -> require(number in -32728.0..32767.0)
DataType.LONG -> require(number in -2147483647.0..2147483647.0)
else -> {}
BaseDataType.UBYTE -> require(number in 0.0..255.0)
BaseDataType.BYTE -> require(number in -128.0..127.0)
BaseDataType.UWORD -> require(number in 0.0..65535.0)
BaseDataType.WORD -> require(number in -32728.0..32767.0)
BaseDataType.LONG -> require(number in -2147483647.0..2147483647.0)
else -> require(type.isNumeric) { "numeric literal type should be numeric: $type" }
}
}
@ -309,7 +307,7 @@ class PtNumber(type: DataType, val number: Double, position: Position) : PtExpre
override fun equals(other: Any?): Boolean {
return if(other==null || other !is PtNumber)
false
else if(type!=DataType.BOOL && other.type!=DataType.BOOL)
else if(!type.isBool && !other.type.isBool)
number==other.number
else
type==other.type && number==other.number
@ -368,7 +366,7 @@ class PtRange(type: DataType, position: Position) : PtExpression(type, position)
}
class PtString(val value: String, val encoding: Encoding, position: Position) : PtExpression(DataType.STR, position) {
class PtString(val value: String, val encoding: Encoding, position: Position) : PtExpression(DataType.forDt(BaseDataType.STR), position) {
override fun hashCode(): Int = Objects.hash(value, encoding)
override fun equals(other: Any?): Boolean {
if(other==null || other !is PtString)
@ -378,7 +376,7 @@ class PtString(val value: String, val encoding: Encoding, position: Position) :
}
class PtTypeCast(type: DataType, position: Position) : PtExpression(type, position) {
class PtTypeCast(type: BaseDataType, position: Position) : PtExpression(DataType.forDt(type), position) {
val value: PtExpression
get() = children.single() as PtExpression
}

View File

@ -1,13 +1,15 @@
package prog8.code.ast
import prog8.code.core.*
import prog8.code.core.DataType
import prog8.code.core.escape
import prog8.code.core.toHex
/**
* Produces readable text from a [PtNode] (AST node, usually starting with PtProgram as root),
* passing it as a String to the specified receiver function.
*/
fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Unit) {
fun type(dt: DataType) = "!${dt.name.lowercase()}!"
fun type(dt: DataType) = "!${dt}!"
fun txt(node: PtNode): String {
return when(node) {
is PtAlign -> "%align ${node.align}"
@ -48,14 +50,14 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
is PtIrRegister -> "IRREG#${node.register} ${type(node.type)}"
is PtMemoryByte -> "@()"
is PtNumber -> {
val numstr = if(node.type == DataType.FLOAT) node.number.toString() else node.number.toHex()
val numstr = if(node.type.isFloat) node.number.toString() else node.number.toHex()
"$numstr ${type(node.type)}"
}
is PtBool -> node.value.toString()
is PtPrefix -> node.operator
is PtRange -> "<range>"
is PtString -> "\"${node.value.escape()}\""
is PtTypeCast -> "as ${node.type.name.lowercase()}"
is PtTypeCast -> "as ${node.type}"
is PtForLoop -> "for"
is PtIfElse -> "ifelse"
is PtIncludeBinary -> "%incbin '${node.file}', ${node.offset}, ${node.length}"
@ -103,21 +105,20 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
"\nblock '${node.name}' $addr"
}
is PtConstant -> {
val value = when(node.type) {
DataType.BOOL -> if(node.value==0.0) "false" else "true"
in IntegerDatatypes -> node.value.toInt().toString()
val value = when {
node.type.isBool -> if(node.value==0.0) "false" else "true"
node.type.isInteger -> node.value.toInt().toString()
else -> node.value.toString()
}
"const ${node.type.name.lowercase()} ${node.name} = $value"
"const ${node.type} ${node.name} = $value"
}
is PtLabel -> "${node.name}:"
is PtMemMapped -> {
if(node.type in ArrayDatatypes) {
if(node.type.isArray) {
val arraysize = if(node.arraySize==null) "" else node.arraySize.toString()
val eltType = ArrayToElementTypes.getValue(node.type)
"&${eltType.name.lowercase()}[$arraysize] ${node.name} = ${node.address.toHex()}"
"&${node.type.elementType()}[$arraysize] ${node.name} = ${node.address.toHex()}"
} else {
"&${node.type.name.lowercase()} ${node.name} = ${node.address.toHex()}"
"&${node.type} ${node.name} = ${node.address.toHex()}"
}
}
is PtSub -> {
@ -127,11 +128,11 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
}
var str = "sub ${node.name}($params) "
if(node.returntype!=null)
str += "-> ${node.returntype.name.lowercase()}"
str += "-> ${node.returntype}"
str
}
is PtVariable -> {
val split = if(node.type in SplitWordArrayTypes) "@split" else ""
val split = if(node.type.isSplitWordArray) "@split" else ""
val align = when(node.align) {
0u -> ""
2u -> "@alignword"
@ -140,15 +141,15 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
else -> throw IllegalArgumentException("invalid alignment size")
}
val str = if(node.arraySize!=null) {
val eltType = ArrayToElementTypes.getValue(node.type)
"${eltType.name.lowercase()}[${node.arraySize}] $split $align ${node.name}"
val eltType = node.type.elementType()
"${eltType}[${node.arraySize}] $split $align ${node.name}"
}
else if(node.type in ArrayDatatypes) {
val eltType = ArrayToElementTypes.getValue(node.type)
"${eltType.name.lowercase()}[] $split $align ${node.name}"
else if(node.type.isArray) {
val eltType = node.type.elementType()
"${eltType}[] $split $align ${node.name}"
}
else
"${node.type.name.lowercase()} ${node.name}"
"${node.type} ${node.name}"
if(node.value!=null)
str + " = " + txt(node.value)
else
@ -161,7 +162,7 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
is PtReturn -> "return"
is PtSubroutineParameter -> {
val reg = if(node.register!=null) "@${node.register}" else ""
"${node.type.name.lowercase()} ${node.name} $reg"
"${node.type} ${node.name} $reg"
}
is PtWhen -> "when"
is PtWhenChoice -> {

View File

@ -30,9 +30,9 @@ class PtSub(
) : PtNamedNode(name, position), IPtSubroutine, IPtStatementContainer {
init {
// params and return value should not be str
if(parameters.any{ it.type !in NumericDatatypes && it.type!=DataType.BOOL })
if(parameters.any{ !it.type.isNumericOrBool })
throw AssemblyError("non-numeric/non-bool parameter")
if(returntype!=null && returntype !in NumericDatatypes && returntype!=DataType.BOOL)
if(returntype!=null && !returntype.isNumericOrBool)
throw AssemblyError("non-numeric/non-bool returntype $returntype")
parameters.forEach { it.parent=this }
}
@ -172,7 +172,7 @@ class PtConstant(name: String, override val type: DataType, val value: Double, p
class PtMemMapped(name: String, override val type: DataType, val address: UInt, val arraySize: UInt?, position: Position) : PtNamedNode(name, position), IPtVariable {
init {
require(type!=DataType.BOOL && type!=DataType.ARRAY_BOOL)
require(!type.isBool && !type.isBoolArray)
}
}

View File

@ -1,7 +1,7 @@
package prog8.code.core
class ReturnConvention(val dt: DataType?, val reg: RegisterOrPair?)
class ParamConvention(val dt: DataType, val reg: RegisterOrPair?, val variable: Boolean)
class ReturnConvention(val dt: BaseDataType?, val reg: RegisterOrPair?)
class ParamConvention(val dt: BaseDataType, val reg: RegisterOrPair?, val variable: Boolean)
class CallConvention(val params: List<ParamConvention>, val returns: ReturnConvention) {
override fun toString(): String {
val paramConvs = params.mapIndexed { index, it ->
@ -21,26 +21,34 @@ class CallConvention(val params: List<ParamConvention>, val returns: ReturnConve
}
}
class FParam(val name: String, vararg val possibleDatatypes: DataType)
class FParam(val name: String, vararg val possibleDatatypes: BaseDataType)
private val IterableDatatypes = arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW)
private val IntegerDatatypes = arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)
private val NumericDatatypes = arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
private val ByteDatatypes = arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE)
private val ArrayDatatypes = arrayOf(BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW)
class FSignature(val pure: Boolean, // does it have side effects?
val returnType: DataType?,
val returnType: BaseDataType?,
vararg val parameters: FParam) {
fun callConvention(actualParamTypes: List<DataType>): CallConvention {
fun callConvention(actualParamTypes: List<BaseDataType>): CallConvention {
val returns: ReturnConvention = when (returnType) {
DataType.UBYTE, DataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A)
DataType.UWORD, DataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY)
DataType.FLOAT -> ReturnConvention(returnType, RegisterOrPair.FAC1)
in PassByReferenceDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY)
BaseDataType.UBYTE, BaseDataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A)
BaseDataType.UWORD, BaseDataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY)
BaseDataType.FLOAT -> ReturnConvention(returnType, RegisterOrPair.FAC1)
in IterableDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY)
null -> ReturnConvention(null, null)
else -> {
// return type depends on arg type
when (val paramType = actualParamTypes.first()) {
DataType.UBYTE, DataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A)
DataType.UWORD, DataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY)
DataType.FLOAT -> ReturnConvention(paramType, RegisterOrPair.FAC1)
in PassByReferenceDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY)
BaseDataType.UBYTE, BaseDataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A)
BaseDataType.UWORD, BaseDataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY)
BaseDataType.FLOAT -> ReturnConvention(paramType, RegisterOrPair.FAC1)
in IterableDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY)
else -> ReturnConvention(paramType, null)
}
}
@ -53,21 +61,21 @@ class FSignature(val pure: Boolean, // does it have side effects?
// this avoids repeated code for every caller to store the value in the subroutine's argument variable.
// (that store is still done, but only coded once at the start at the subroutine itself rather than at every call site).
val paramConv = when(val paramType = actualParamTypes[0]) {
DataType.UBYTE, DataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false)
DataType.UWORD, DataType.WORD -> ParamConvention(paramType, RegisterOrPair.AY, false)
DataType.FLOAT -> ParamConvention(paramType, RegisterOrPair.AY, false) // NOTE: for builtin functions, floating point arguments are passed by reference (so you get a pointer in AY)
in PassByReferenceDatatypes -> ParamConvention(paramType, RegisterOrPair.AY, false)
BaseDataType.UBYTE, BaseDataType.BYTE -> ParamConvention(paramType, RegisterOrPair.A, false)
BaseDataType.UWORD, BaseDataType.WORD -> ParamConvention(paramType, RegisterOrPair.AY, false)
BaseDataType.FLOAT -> ParamConvention(paramType, RegisterOrPair.AY, false) // NOTE: for builtin functions, floating point arguments are passed by reference (so you get a pointer in AY)
in IterableDatatypes -> ParamConvention(paramType, RegisterOrPair.AY, false)
else -> ParamConvention(paramType, null, false)
}
CallConvention(listOf(paramConv), returns)
}
actualParamTypes.size==2 && (actualParamTypes[0] in ByteDatatypes && actualParamTypes[1] in WordDatatypes) -> {
actualParamTypes.size==2 && (actualParamTypes[0] in ByteDatatypes && actualParamTypes[1].isWord) -> {
TODO("opportunity to pass word+byte arguments in A,Y and X registers but not implemented yet")
}
actualParamTypes.size==2 && (actualParamTypes[0] in WordDatatypes && actualParamTypes[1] in ByteDatatypes) -> {
actualParamTypes.size==2 && (actualParamTypes[0].isWord && actualParamTypes[1].isByte) -> {
TODO("opportunity to pass word+byte arguments in A,Y and X registers but not implemented yet")
}
actualParamTypes.size==3 && actualParamTypes.all { it in ByteDatatypes } -> {
actualParamTypes.size==3 && actualParamTypes.all { it.isByte } -> {
TODO("opportunity to pass 3 byte arguments in A,Y and X registers but not implemented yet")
}
else -> {
@ -80,65 +88,65 @@ class FSignature(val pure: Boolean, // does it have side effects?
}
val BuiltinFunctions: Map<String, FSignature> = mapOf(
"setlsb" to FSignature(false, null, FParam("variable", DataType.WORD, DataType.UWORD), FParam("value", DataType.BYTE, DataType.UBYTE)),
"setmsb" to FSignature(false, null, FParam("variable", DataType.WORD, DataType.UWORD), FParam("value", DataType.BYTE, DataType.UBYTE)),
"rol" to FSignature(false, null, FParam("item", DataType.UBYTE, DataType.UWORD)),
"ror" to FSignature(false, null, FParam("item", DataType.UBYTE, DataType.UWORD)),
"rol2" to FSignature(false, null, FParam("item", DataType.UBYTE, DataType.UWORD)),
"ror2" to FSignature(false, null, FParam("item", DataType.UBYTE, DataType.UWORD)),
"setlsb" to FSignature(false, null, FParam("variable", BaseDataType.WORD, BaseDataType.UWORD), FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
"setmsb" to FSignature(false, null, FParam("variable", BaseDataType.WORD, BaseDataType.UWORD), FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
"rol" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
"ror" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
"rol2" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
"ror2" to FSignature(false, null, FParam("item", BaseDataType.UBYTE, BaseDataType.UWORD)),
"cmp" to FSignature(false, null, FParam("value1", *IntegerDatatypes), FParam("value2", *NumericDatatypes)), // cmp returns a status in the carry flag, but not a proper return value
"prog8_lib_stringcompare" to FSignature(true, DataType.BYTE, FParam("str1", DataType.STR), FParam("str2", DataType.STR)),
"prog8_lib_square_byte" to FSignature(true, DataType.UBYTE, FParam("value", DataType.BYTE, DataType.UBYTE)),
"prog8_lib_square_word" to FSignature(true, DataType.UWORD, FParam("value", DataType.WORD, DataType.UWORD)),
"prog8_ifelse_bittest_set" to FSignature(true, DataType.BOOL, FParam("variable", *ByteDatatypes), FParam("bitnumber", DataType.UBYTE)),
"prog8_ifelse_bittest_notset" to FSignature(true, DataType.BOOL, FParam("variable", *ByteDatatypes), FParam("bitnumber", DataType.UBYTE)),
"prog8_lib_stringcompare" to FSignature(true, BaseDataType.BYTE, FParam("str1", BaseDataType.STR), FParam("str2", BaseDataType.STR)),
"prog8_lib_square_byte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.BYTE, BaseDataType.UBYTE)),
"prog8_lib_square_word" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.WORD, BaseDataType.UWORD)),
"prog8_ifelse_bittest_set" to FSignature(true, BaseDataType.BOOL, FParam("variable", *ByteDatatypes), FParam("bitnumber", BaseDataType.UBYTE)),
"prog8_ifelse_bittest_notset" to FSignature(true, BaseDataType.BOOL, FParam("variable", *ByteDatatypes), FParam("bitnumber", BaseDataType.UBYTE)),
"abs" to FSignature(true, null, FParam("value", *NumericDatatypes)),
"abs__byte" to FSignature(true, DataType.BYTE, FParam("value", DataType.BYTE)),
"abs__word" to FSignature(true, DataType.WORD, FParam("value", DataType.WORD)),
"abs__float" to FSignature(true, DataType.FLOAT, FParam("value", DataType.FLOAT)),
"len" to FSignature(true, DataType.UWORD, FParam("values", *IterableDatatypes)),
"sizeof" to FSignature(true, DataType.UBYTE, FParam("object", *DataType.entries.toTypedArray())),
"sgn" to FSignature(true, DataType.BYTE, FParam("value", *NumericDatatypes)),
"abs__byte" to FSignature(true, BaseDataType.BYTE, FParam("value", BaseDataType.BYTE)),
"abs__word" to FSignature(true, BaseDataType.WORD, FParam("value", BaseDataType.WORD)),
"abs__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
"len" to FSignature(true, BaseDataType.UWORD, FParam("values", *IterableDatatypes)),
"sizeof" to FSignature(true, BaseDataType.UBYTE, FParam("object", *BaseDataType.entries.toTypedArray())),
"sgn" to FSignature(true, BaseDataType.BYTE, FParam("value", *NumericDatatypes)),
"sqrt" to FSignature(true, null, FParam("value", *NumericDatatypes)),
"sqrt__ubyte" to FSignature(true, DataType.UBYTE, FParam("value", DataType.UBYTE)),
"sqrt__uword" to FSignature(true, DataType.UBYTE, FParam("value", DataType.UWORD)),
"sqrt__float" to FSignature(true, DataType.FLOAT, FParam("value", DataType.FLOAT)),
"divmod" to FSignature(false, null, FParam("dividend", DataType.UBYTE, DataType.UWORD), FParam("divisor", DataType.UBYTE, DataType.UWORD), FParam("quotient", DataType.UBYTE, DataType.UWORD), FParam("remainder", DataType.UBYTE, DataType.UWORD)),
"divmod__ubyte" to FSignature(false, null, FParam("dividend", DataType.UBYTE), FParam("divisor", DataType.UBYTE), FParam("quotient", DataType.UBYTE), FParam("remainder", DataType.UBYTE)),
"divmod__uword" to FSignature(false, null, FParam("dividend", DataType.UWORD), FParam("divisor", DataType.UWORD), FParam("quotient", DataType.UWORD), FParam("remainder", DataType.UWORD)),
"lsb" to FSignature(true, DataType.UBYTE, FParam("value", DataType.UWORD, DataType.WORD, DataType.LONG)),
"lsw" to FSignature(true, DataType.UWORD, FParam("value", DataType.UWORD, DataType.WORD, DataType.LONG)),
"msb" to FSignature(true, DataType.UBYTE, FParam("value", DataType.UWORD, DataType.WORD, DataType.LONG)),
"msw" to FSignature(true, DataType.UWORD, FParam("value", DataType.UWORD, DataType.WORD, DataType.LONG)),
"mkword" to FSignature(true, DataType.UWORD, FParam("msb", DataType.UBYTE), FParam("lsb", DataType.UBYTE)),
"clamp" to FSignature(true, null, FParam("value", DataType.BYTE), FParam("minimum", DataType.BYTE), FParam("maximum", DataType.BYTE)),
"clamp__byte" to FSignature(true, DataType.BYTE, FParam("value", DataType.BYTE), FParam("minimum", DataType.BYTE), FParam("maximum", DataType.BYTE)),
"clamp__ubyte" to FSignature(true, DataType.UBYTE, FParam("value", DataType.UBYTE), FParam("minimum", DataType.UBYTE), FParam("maximum", DataType.UBYTE)),
"clamp__word" to FSignature(true, DataType.WORD, FParam("value", DataType.WORD), FParam("minimum", DataType.WORD), FParam("maximum", DataType.WORD)),
"clamp__uword" to FSignature(true, DataType.UWORD, FParam("value", DataType.UWORD), FParam("minimum", DataType.UWORD), FParam("maximum", DataType.UWORD)),
"min" to FSignature(true, null, FParam("val1", DataType.BYTE), FParam("val2", DataType.BYTE)),
"min__byte" to FSignature(true, DataType.BYTE, FParam("val1", DataType.BYTE), FParam("val2", DataType.BYTE)),
"min__ubyte" to FSignature(true, DataType.UBYTE, FParam("val1", DataType.UBYTE), FParam("val2", DataType.UBYTE)),
"min__word" to FSignature(true, DataType.WORD, FParam("val1", DataType.WORD), FParam("val2", DataType.WORD)),
"min__uword" to FSignature(true, DataType.UWORD, FParam("val1", DataType.UWORD), FParam("val2", DataType.UWORD)),
"max" to FSignature(true, null, FParam("val1", DataType.BYTE), FParam("val2", DataType.BYTE)),
"max__byte" to FSignature(true, DataType.BYTE, FParam("val1", DataType.BYTE), FParam("val2", DataType.BYTE)),
"max__ubyte" to FSignature(true, DataType.UBYTE, FParam("val1", DataType.UBYTE), FParam("val2", DataType.UBYTE)),
"max__word" to FSignature(true, DataType.WORD, FParam("val1", DataType.WORD), FParam("val2", DataType.WORD)),
"max__uword" to FSignature(true, DataType.UWORD, FParam("val1", DataType.UWORD), FParam("val2", DataType.UWORD)),
"peek" to FSignature(true, DataType.UBYTE, FParam("address", DataType.UWORD)),
"peekw" to FSignature(true, DataType.UWORD, FParam("address", DataType.UWORD)),
"peekf" to FSignature(true, DataType.FLOAT, FParam("address", DataType.UWORD)),
"poke" to FSignature(false, null, FParam("address", DataType.UWORD), FParam("value", DataType.UBYTE)),
"pokew" to FSignature(false, null, FParam("address", DataType.UWORD), FParam("value", DataType.UWORD)),
"pokef" to FSignature(false, null, FParam("address", DataType.UWORD), FParam("value", DataType.FLOAT)),
"pokemon" to FSignature(false, DataType.UBYTE, FParam("address", DataType.UWORD), FParam("value", DataType.UBYTE)),
"sqrt__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE)),
"sqrt__uword" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD)),
"sqrt__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
"divmod" to FSignature(false, null, FParam("dividend", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("divisor", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("quotient", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("remainder", BaseDataType.UBYTE, BaseDataType.UWORD)),
"divmod__ubyte" to FSignature(false, null, FParam("dividend", BaseDataType.UBYTE), FParam("divisor", BaseDataType.UBYTE), FParam("quotient", BaseDataType.UBYTE), FParam("remainder", BaseDataType.UBYTE)),
"divmod__uword" to FSignature(false, null, FParam("dividend", BaseDataType.UWORD), FParam("divisor", BaseDataType.UWORD), FParam("quotient", BaseDataType.UWORD), FParam("remainder", BaseDataType.UWORD)),
"lsb" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
"lsw" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
"msb" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
"msw" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)),
"mkword" to FSignature(true, BaseDataType.UWORD, FParam("msb", BaseDataType.UBYTE), FParam("lsb", BaseDataType.UBYTE)),
"clamp" to FSignature(true, null, FParam("value", BaseDataType.BYTE), FParam("minimum", BaseDataType.BYTE), FParam("maximum", BaseDataType.BYTE)),
"clamp__byte" to FSignature(true, BaseDataType.BYTE, FParam("value", BaseDataType.BYTE), FParam("minimum", BaseDataType.BYTE), FParam("maximum", BaseDataType.BYTE)),
"clamp__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE), FParam("minimum", BaseDataType.UBYTE), FParam("maximum", BaseDataType.UBYTE)),
"clamp__word" to FSignature(true, BaseDataType.WORD, FParam("value", BaseDataType.WORD), FParam("minimum", BaseDataType.WORD), FParam("maximum", BaseDataType.WORD)),
"clamp__uword" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.UWORD), FParam("minimum", BaseDataType.UWORD), FParam("maximum", BaseDataType.UWORD)),
"min" to FSignature(true, null, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
"min__byte" to FSignature(true, BaseDataType.BYTE, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
"min__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("val1", BaseDataType.UBYTE), FParam("val2", BaseDataType.UBYTE)),
"min__word" to FSignature(true, BaseDataType.WORD, FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)),
"min__uword" to FSignature(true, BaseDataType.UWORD, FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)),
"max" to FSignature(true, null, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
"max__byte" to FSignature(true, BaseDataType.BYTE, FParam("val1", BaseDataType.BYTE), FParam("val2", BaseDataType.BYTE)),
"max__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("val1", BaseDataType.UBYTE), FParam("val2", BaseDataType.UBYTE)),
"max__word" to FSignature(true, BaseDataType.WORD, FParam("val1", BaseDataType.WORD), FParam("val2", BaseDataType.WORD)),
"max__uword" to FSignature(true, BaseDataType.UWORD, FParam("val1", BaseDataType.UWORD), FParam("val2", BaseDataType.UWORD)),
"peek" to FSignature(true, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD)),
"peekw" to FSignature(true, BaseDataType.UWORD, FParam("address", BaseDataType.UWORD)),
"peekf" to FSignature(true, BaseDataType.FLOAT, FParam("address", BaseDataType.UWORD)),
"poke" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)),
"pokew" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UWORD)),
"pokef" to FSignature(false, null, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.FLOAT)),
"pokemon" to FSignature(false, BaseDataType.UBYTE, FParam("address", BaseDataType.UWORD), FParam("value", BaseDataType.UBYTE)),
"rsave" to FSignature(false, null),
"rrestore" to FSignature(false, null),
"memory" to FSignature(true, DataType.UWORD, FParam("name", DataType.STR), FParam("size", DataType.UWORD), FParam("alignment", DataType.UWORD)),
"callfar" to FSignature(false, DataType.UWORD, FParam("bank", DataType.UBYTE), FParam("address", DataType.UWORD), FParam("arg", DataType.UWORD)),
"callfar2" to FSignature(false, DataType.UWORD, FParam("bank", DataType.UBYTE), FParam("address", DataType.UWORD), FParam("argA", DataType.UBYTE), FParam("argX", DataType.UBYTE), FParam("argY", DataType.UBYTE), FParam("argC", DataType.BOOL)),
"call" to FSignature(false, DataType.UWORD, FParam("address", DataType.UWORD)),
"memory" to FSignature(true, BaseDataType.UWORD, FParam("name", BaseDataType.STR), FParam("size", BaseDataType.UWORD), FParam("alignment", BaseDataType.UWORD)),
"callfar" to FSignature(false, BaseDataType.UWORD, FParam("bank", BaseDataType.UBYTE), FParam("address", BaseDataType.UWORD), FParam("arg", BaseDataType.UWORD)),
"callfar2" to FSignature(false, BaseDataType.UWORD, FParam("bank", BaseDataType.UBYTE), FParam("address", BaseDataType.UWORD), FParam("argA", BaseDataType.UBYTE), FParam("argX", BaseDataType.UBYTE), FParam("argY", BaseDataType.UBYTE), FParam("argC", BaseDataType.BOOL)),
"call" to FSignature(false, BaseDataType.UWORD, FParam("address", BaseDataType.UWORD)),
)
val InplaceModifyingBuiltinFunctions = setOf(

View File

@ -1,6 +1,8 @@
package prog8.code.core
enum class DataType {
import java.util.Objects
enum class BaseDataType {
UBYTE, // pass by value 8 bits unsigned
BYTE, // pass by value 8 bits signed
UWORD, // pass by value 16 bits unsigned
@ -9,55 +11,246 @@ enum class DataType {
FLOAT, // pass by value machine dependent
BOOL, // pass by value bit 0 of an 8-bit byte
STR, // pass by reference
ARRAY_UB, // pass by reference
ARRAY_B, // pass by reference
ARRAY_UW, // pass by reference
ARRAY_UW_SPLIT, // pass by reference, lo/hi byte split
ARRAY_W, // pass by reference
ARRAY_W_SPLIT, // pass by reference, lo/hi byte split
ARRAY_F, // pass by reference
ARRAY_BOOL, // pass by reference
ARRAY, // pass by reference, subtype is the element type
ARRAY_SPLITW, // pass by reference, split word layout, subtype is the element type (restricted to word types)
UNDEFINED;
/**
* is the type assignable to the given other type (perhaps via a typecast) without loss of precision?
*/
infix fun isAssignableTo(targetType: DataType) =
when(this) {
BOOL -> targetType == BOOL
UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, LONG, FLOAT)
BYTE -> targetType.oneOf(BYTE, WORD, LONG, FLOAT)
UWORD -> targetType.oneOf(UWORD, LONG, FLOAT)
WORD -> targetType.oneOf(WORD, LONG, FLOAT)
LONG -> targetType.oneOf(LONG, FLOAT)
FLOAT -> targetType.oneOf(FLOAT)
STR -> targetType.oneOf(STR, UWORD)
in ArrayDatatypes -> targetType == this
else -> false
}
fun oneOf(vararg types: DataType) = this in types
infix fun largerThan(other: DataType) =
fun largerSizeThan(other: BaseDataType) =
when {
this == other -> false
this in ByteDatatypesWithBoolean -> false
this in WordDatatypes -> other in ByteDatatypesWithBoolean
this == LONG -> other in ByteDatatypesWithBoolean+WordDatatypes
this.isByteOrBool -> false
this.isWord -> other.isByteOrBool
this == LONG -> other.isByteOrBool || other.isWord
this == STR && other == UWORD || this == UWORD && other == STR -> false
this.isArray -> other != FLOAT
this == STR -> other != FLOAT
else -> true
}
infix fun equalsSize(other: DataType) =
fun equalsSize(other: BaseDataType) =
when {
this == other -> true
this in ByteDatatypesWithBoolean -> other in ByteDatatypesWithBoolean
this in WordDatatypes -> other in WordDatatypes
this== STR && other== UWORD || this== UWORD && other== STR -> true
this.isByteOrBool -> other.isByteOrBool
this.isWord -> other.isWord
this == STR && other== UWORD || this== UWORD && other== STR -> true
this == STR && other.isArray -> true
this.isArray && other == STR -> true
else -> false
}
}
val BaseDataType.isByte get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE)
val BaseDataType.isByteOrBool get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.BOOL)
val BaseDataType.isWord get() = this in arrayOf(BaseDataType.UWORD, BaseDataType.WORD)
val BaseDataType.isInteger get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG)
val BaseDataType.isIntegerOrBool get() = this in arrayOf(BaseDataType.UBYTE, BaseDataType.BYTE, BaseDataType.UWORD, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.BOOL)
val BaseDataType.isNumeric get() = this == BaseDataType.FLOAT || this.isInteger
val BaseDataType.isNumericOrBool get() = this == BaseDataType.BOOL || this.isNumeric
val BaseDataType.isSigned get() = this in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
val BaseDataType.isArray get() = this == BaseDataType.ARRAY || this == BaseDataType.ARRAY_SPLITW
val BaseDataType.isSplitWordArray get() = this == BaseDataType.ARRAY_SPLITW
val BaseDataType.isIterable get() = this in arrayOf(BaseDataType.STR, BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW)
val BaseDataType.isPassByRef get() = this.isIterable
val BaseDataType.isPassByValue get() = !this.isIterable
sealed class SubType(val dt: BaseDataType) {
companion object {
private val types by lazy {
// lazy because of static initialization order
mapOf(
BaseDataType.UBYTE to SubUnsignedByte,
BaseDataType.BYTE to SubSignedByte,
BaseDataType.UWORD to SubUnsignedWord,
BaseDataType.WORD to SubSignedWord,
BaseDataType.FLOAT to SubFloat,
BaseDataType.BOOL to SubBool
)}
fun forDt(dt: BaseDataType) = types.getValue(dt)
}
}
private data object SubUnsignedByte: SubType(BaseDataType.UBYTE)
private data object SubSignedByte: SubType(BaseDataType.BYTE)
private data object SubUnsignedWord: SubType(BaseDataType.UWORD)
private data object SubSignedWord: SubType(BaseDataType.WORD)
private data object SubBool: SubType(BaseDataType.BOOL)
private data object SubFloat: SubType(BaseDataType.FLOAT)
class DataType private constructor(val base: BaseDataType, val sub: SubType?) {
init {
if(base.isArray) {
require(sub != null)
if(base.isSplitWordArray)
require(sub.dt == BaseDataType.UWORD || sub.dt == BaseDataType.WORD)
}
else if(base==BaseDataType.STR)
require(sub?.dt==BaseDataType.UBYTE) { "STR subtype should be ubyte" }
else
require(sub == null)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is DataType) return false
return base == other.base && sub == other.sub
}
override fun hashCode(): Int = Objects.hash(base, sub)
companion object {
private val simpletypes = mapOf(
BaseDataType.UBYTE to DataType(BaseDataType.UBYTE, null),
BaseDataType.BYTE to DataType(BaseDataType.BYTE, null),
BaseDataType.UWORD to DataType(BaseDataType.UWORD, null),
BaseDataType.WORD to DataType(BaseDataType.WORD, null),
BaseDataType.LONG to DataType(BaseDataType.LONG, null),
BaseDataType.FLOAT to DataType(BaseDataType.FLOAT, null),
BaseDataType.BOOL to DataType(BaseDataType.BOOL, null),
BaseDataType.STR to DataType(BaseDataType.STR, SubUnsignedByte),
BaseDataType.UNDEFINED to DataType(BaseDataType.UNDEFINED, null)
)
fun forDt(dt: BaseDataType) = simpletypes.getValue(dt)
fun arrayFor(elementDt: BaseDataType, split: Boolean=false): DataType {
val actualElementDt = if(elementDt==BaseDataType.STR) BaseDataType.UWORD else elementDt // array of strings is actually just an array of UWORD pointers
if(split) return DataType(BaseDataType.ARRAY_SPLITW, SubType.forDt(actualElementDt))
else return DataType(BaseDataType.ARRAY, SubType.forDt(actualElementDt))
}
}
fun elementToArray(split: Boolean = false): DataType {
if(split) {
return if (base == BaseDataType.UWORD || base == BaseDataType.WORD || base == BaseDataType.STR) arrayFor(base, true)
else throw IllegalArgumentException("invalid split array elt dt")
}
return arrayFor(base)
}
fun elementType(): DataType =
if(base.isArray || base==BaseDataType.STR)
forDt(sub!!.dt)
else
throw IllegalArgumentException("not an array")
override fun toString(): String = when(base) {
BaseDataType.ARRAY -> {
when(sub) {
SubBool -> "bool[]"
SubFloat -> "float[]"
SubSignedByte -> "byte[]"
SubSignedWord -> "word[]"
SubUnsignedByte -> "ubyte[]"
SubUnsignedWord -> "uword[]"
null -> throw IllegalArgumentException("invalid sub type")
}
}
BaseDataType.ARRAY_SPLITW -> {
when(sub) {
SubSignedWord -> "@split word[]"
SubUnsignedWord -> "@split uword[]"
else -> throw IllegalArgumentException("invalid sub type")
}
}
else -> base.name.lowercase()
}
fun sourceString(): String = when (base) {
BaseDataType.BOOL -> "bool"
BaseDataType.UBYTE -> "ubyte"
BaseDataType.BYTE -> "byte"
BaseDataType.UWORD -> "uword"
BaseDataType.WORD -> "word"
BaseDataType.LONG -> "long"
BaseDataType.FLOAT -> "float"
BaseDataType.STR -> "str"
BaseDataType.ARRAY -> {
when(sub) {
SubUnsignedByte -> "ubyte["
SubUnsignedWord -> "uword["
SubBool -> "bool["
SubSignedByte -> "byte["
SubSignedWord -> "word["
SubFloat -> "float["
null -> throw IllegalArgumentException("invalid sub type")
}
}
BaseDataType.ARRAY_SPLITW -> {
when(sub) {
SubUnsignedWord -> "@split uword["
SubSignedWord -> "@split word["
else -> throw IllegalArgumentException("invalid sub type")
}
}
BaseDataType.UNDEFINED -> throw IllegalArgumentException("wrong dt")
}
// is the type assignable to the given other type (perhaps via a typecast) without loss of precision?
infix fun isAssignableTo(targetType: DataType) =
when(base) {
BaseDataType.BOOL -> targetType.base == BaseDataType.BOOL
BaseDataType.UBYTE -> targetType.base in arrayOf(BaseDataType.UBYTE, BaseDataType.WORD, BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT)
BaseDataType.BYTE -> targetType.base in arrayOf(BaseDataType.BYTE, BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
BaseDataType.UWORD -> targetType.base in arrayOf(BaseDataType.UWORD, BaseDataType.LONG, BaseDataType.FLOAT)
BaseDataType.WORD -> targetType.base in arrayOf(BaseDataType.WORD, BaseDataType.LONG, BaseDataType.FLOAT)
BaseDataType.LONG -> targetType.base in arrayOf(BaseDataType.LONG, BaseDataType.FLOAT)
BaseDataType.FLOAT -> targetType.base in arrayOf(BaseDataType.FLOAT)
BaseDataType.STR -> targetType.base in arrayOf(BaseDataType.STR, BaseDataType.UWORD)
BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW -> targetType.base in arrayOf(BaseDataType.ARRAY, BaseDataType.ARRAY_SPLITW) && targetType.sub == sub
BaseDataType.UNDEFINED -> false
}
fun largerSizeThan(other: DataType): Boolean {
if(isArray) throw IllegalArgumentException("cannot compare size of array types")
return base.largerSizeThan(other.base)
}
fun equalsSize(other: DataType): Boolean {
if(isArray) throw IllegalArgumentException("cannot compare size of array types")
return base.equalsSize(other.base)
}
val isUndefined = base == BaseDataType.UNDEFINED
val isByte = base.isByte
val isUnsignedByte = base == BaseDataType.UBYTE
val isSignedByte = base == BaseDataType.BYTE
val isByteOrBool = base.isByteOrBool
val isWord = base.isWord
val isUnsignedWord = base == BaseDataType.UWORD
val isSignedWord = base == BaseDataType.WORD
val isInteger = base.isInteger
val isIntegerOrBool = base.isIntegerOrBool
val isNumeric = base.isNumeric
val isNumericOrBool = base.isNumericOrBool
val isSigned = base.isSigned
val isUnsigned = !base.isSigned
val isArray = base.isArray
val isBoolArray = base.isArray && sub?.dt == BaseDataType.BOOL
val isByteArray = base.isArray && (sub?.dt == BaseDataType.UBYTE || sub?.dt == BaseDataType.BYTE)
val isUnsignedByteArray = base.isArray && sub?.dt == BaseDataType.UBYTE
val isSignedByteArray = base.isArray && sub?.dt == BaseDataType.BYTE
val isWordArray = base.isArray && (sub?.dt == BaseDataType.UWORD || sub?.dt == BaseDataType.WORD)
val isUnsignedWordArray = base.isArray && sub?.dt == BaseDataType.UWORD
val isSignedWordArray = base.isArray && sub?.dt == BaseDataType.WORD
val isFloatArray = base.isArray && sub?.dt == BaseDataType.FLOAT
val isString = base == BaseDataType.STR
val isBool = base == BaseDataType.BOOL
val isFloat = base == BaseDataType.FLOAT
val isLong = base == BaseDataType.LONG
val isStringly = base == BaseDataType.STR || base == BaseDataType.UWORD || (base == BaseDataType.ARRAY && (sub?.dt == BaseDataType.UBYTE || sub?.dt == BaseDataType.BYTE))
val isSplitWordArray = base.isSplitWordArray
val isSplitUnsignedWordArray = base.isSplitWordArray && sub?.dt == BaseDataType.UWORD
val isSplitSignedWordArray = base.isSplitWordArray && sub?.dt == BaseDataType.WORD
val isIterable = base.isIterable
val isPassByRef = base.isPassByRef
val isPassByValue = base.isPassByValue
}
enum class CpuRegister {
A,
X,
@ -97,11 +290,11 @@ enum class RegisterOrPair {
fun asScopedNameVirtualReg(type: DataType?): List<String> {
require(this in Cx16VirtualRegisters)
val suffix = when(type) {
DataType.UBYTE, DataType.BOOL -> "L"
DataType.BYTE -> "sL"
DataType.WORD -> "s"
DataType.UWORD, null -> ""
val suffix = when(type?.base) {
BaseDataType.UBYTE, BaseDataType.BOOL -> "L"
BaseDataType.BYTE -> "sL"
BaseDataType.WORD -> "s"
BaseDataType.UWORD, null -> ""
else -> throw kotlin.IllegalArgumentException("invalid register param type")
}
return listOf("cx16", name.lowercase()+suffix)
@ -134,48 +327,6 @@ enum class BranchCondition {
VC
}
val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE)
val ByteDatatypesWithBoolean = ByteDatatypes + DataType.BOOL
val WordDatatypes = arrayOf(DataType.UWORD, DataType.WORD)
val IntegerDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG)
val IntegerDatatypesWithBoolean = IntegerDatatypes + DataType.BOOL
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.LONG, DataType.FLOAT)
val NumericDatatypesWithBoolean = NumericDatatypes + DataType.BOOL
val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.LONG, DataType.FLOAT)
val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT, DataType.ARRAY_F, DataType.ARRAY_BOOL)
val StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD)
val SplitWordArrayTypes = arrayOf(DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT)
val IterableDatatypes = arrayOf(
DataType.STR,
DataType.ARRAY_UB, DataType.ARRAY_B,
DataType.ARRAY_UW, DataType.ARRAY_W,
DataType.ARRAY_UW_SPLIT, DataType.ARRAY_W_SPLIT,
DataType.ARRAY_F, DataType.ARRAY_BOOL
)
val PassByValueDatatypes = NumericDatatypesWithBoolean
val PassByReferenceDatatypes = IterableDatatypes
val ArrayToElementTypes = mapOf(
DataType.STR to DataType.UBYTE,
DataType.ARRAY_B to DataType.BYTE,
DataType.ARRAY_UB to DataType.UBYTE,
DataType.ARRAY_W to DataType.WORD,
DataType.ARRAY_UW to DataType.UWORD,
DataType.ARRAY_W_SPLIT to DataType.WORD,
DataType.ARRAY_UW_SPLIT to DataType.UWORD,
DataType.ARRAY_F to DataType.FLOAT,
DataType.ARRAY_BOOL to DataType.BOOL
)
val ElementToArrayTypes = mapOf(
DataType.BYTE to DataType.ARRAY_B,
DataType.UBYTE to DataType.ARRAY_UB,
DataType.WORD to DataType.ARRAY_W,
DataType.UWORD to DataType.ARRAY_UW,
DataType.FLOAT to DataType.ARRAY_F,
DataType.BOOL to DataType.ARRAY_BOOL,
DataType.STR to DataType.ARRAY_UW // array of str is just an array of pointers
)
val Cx16VirtualRegisters = arrayOf(
RegisterOrPair.R0, RegisterOrPair.R1, RegisterOrPair.R2, RegisterOrPair.R3,
RegisterOrPair.R4, RegisterOrPair.R5, RegisterOrPair.R6, RegisterOrPair.R7,

View File

@ -1,6 +1,6 @@
package prog8.code.core
interface IMemSizer {
fun memorySize(dt: DataType): Int
fun memorySize(arrayDt: DataType, numElements: Int): Int
fun memorySize(dt: DataType, numElements: Int?): Int
fun memorySize(dt: SubType): Int
}

View File

@ -70,9 +70,9 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
return Err(MemAllocationError("zero page usage has been disabled"))
val size: Int =
when (datatype) {
in IntegerDatatypesWithBoolean -> options.compTarget.memorySize(datatype)
DataType.STR, in ArrayDatatypes -> {
when {
datatype.isIntegerOrBool -> options.compTarget.memorySize(datatype, null)
datatype.isString || datatype.isArray -> {
val memsize = options.compTarget.memorySize(datatype, numElements!!)
if(position!=null)
errors.warn("allocating a large value in zeropage; str/array $memsize bytes", position)
@ -80,9 +80,9 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
errors.warn("$name: allocating a large value in zeropage; str/array $memsize bytes", Position.DUMMY)
memsize
}
DataType.FLOAT -> {
datatype.isFloat -> {
if (options.floats) {
val memsize = options.compTarget.memorySize(DataType.FLOAT)
val memsize = options.compTarget.memorySize(DataType.forDt(BaseDataType.FLOAT), null)
if(position!=null)
errors.warn("allocating a large value in zeropage; float $memsize bytes", position)
else
@ -118,10 +118,10 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
require(size>=0)
free.removeAll(address until address+size.toUInt())
if(name.isNotEmpty()) {
allocatedVariables[name] = when(datatype) {
in NumericDatatypes, DataType.BOOL -> VarAllocation(address, datatype, size) // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments
DataType.STR -> VarAllocation(address, datatype, size)
in ArrayDatatypes -> VarAllocation(address, datatype, size)
allocatedVariables[name] = when {
datatype.isNumericOrBool -> VarAllocation(address, datatype, size) // numerical variables in zeropage never have an initial value here because they are set in separate initializer assignments
datatype.isString -> VarAllocation(address, datatype, size)
datatype.isArray -> VarAllocation(address, datatype, size)
else -> throw AssemblyError("invalid dt")
}
}
@ -150,14 +150,13 @@ class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAlloc
errors: IErrorReporter): Result<VarAllocation, MemAllocationError> {
val size: Int =
when (datatype) {
in IntegerDatatypesWithBoolean -> options.compTarget.memorySize(datatype)
DataType.STR, in ArrayDatatypes -> {
options.compTarget.memorySize(datatype, numElements!!)
}
DataType.FLOAT -> {
when {
datatype.isIntegerOrBool -> options.compTarget.memorySize(datatype, null)
datatype.isString -> numElements!!
datatype.isArray -> options.compTarget.memorySize(datatype, numElements!!)
datatype.isFloat -> {
if (options.floats) {
options.compTarget.memorySize(DataType.FLOAT)
options.compTarget.memorySize(DataType.forDt(BaseDataType.FLOAT), null)
} else return Err(MemAllocationError("floating point option not enabled"))
}
else -> throw MemAllocationError("weird dt")

View File

@ -60,7 +60,7 @@ private fun optimizeAssignTargets(program: PtProgram, st: SymbolTable, errors: I
if(node.children.dropLast(1).all { (it as PtAssignTarget).void }) {
// all targets are now void, the whole assignment can be discarded and replaced by just a (void) call to the subroutine
val index = node.parent.children.indexOf(node)
val voidCall = PtFunctionCall(functionName, true, DataType.UNDEFINED, value.position)
val voidCall = PtFunctionCall(functionName, true, DataType.forDt(BaseDataType.UNDEFINED), value.position)
value.children.forEach { voidCall.add(it) }
node.parent.children[index] = voidCall
voidCall.parent = node.parent
@ -81,12 +81,12 @@ private fun optimizeBitTest(program: PtProgram, options: CompilationOptions): In
fun makeBittestCall(condition: PtBinaryExpression, and: PtBinaryExpression, variable: PtIdentifier, bitmask: Int): PtBuiltinFunctionCall {
require(bitmask==128 || bitmask==64)
val setOrNot = if(condition.operator=="!=") "set" else "notset"
val bittestCall = PtBuiltinFunctionCall("prog8_ifelse_bittest_$setOrNot", false, true, DataType.BOOL, condition.position)
val bittestCall = PtBuiltinFunctionCall("prog8_ifelse_bittest_$setOrNot", false, true, DataType.forDt(BaseDataType.BOOL), condition.position)
bittestCall.add(variable)
if(bitmask==128)
bittestCall.add(PtNumber(DataType.UBYTE, 7.0, and.right.position))
bittestCall.add(PtNumber(BaseDataType.UBYTE, 7.0, and.right.position))
else
bittestCall.add(PtNumber(DataType.UBYTE, 6.0, and.right.position))
bittestCall.add(PtNumber(BaseDataType.UBYTE, 6.0, and.right.position))
return bittestCall
}
@ -94,17 +94,17 @@ private fun optimizeBitTest(program: PtProgram, options: CompilationOptions): In
if(condition!=null && (condition.operator=="==" || condition.operator=="!=")) {
if (condition.right.asConstInteger() == 0) {
val and = condition.left as? PtBinaryExpression
if (and != null && and.operator == "&" && and.type == DataType.UBYTE) {
if (and != null && and.operator == "&" && and.type.isUnsignedByte) {
val bitmask = and.right.asConstInteger()
if(bitmask==128 || bitmask==64) {
val