mirror of
https://github.com/irmen/prog8.git
synced 2024-12-23 09:32:43 +00:00
consolidate builtin function definitions into codeCore
This commit is contained in:
parent
99c62aab36
commit
117d848466
111
codeCore/src/prog8/code/core/BuiltinFunctions.kt
Normal file
111
codeCore/src/prog8/code/core/BuiltinFunctions.kt
Normal file
@ -0,0 +1,111 @@
|
||||
package prog8.code.core
|
||||
|
||||
class ReturnConvention(val dt: DataType?, val reg: RegisterOrPair?, val floatFac1: Boolean)
|
||||
class ParamConvention(val dt: DataType, 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 ->
|
||||
when {
|
||||
it.reg!=null -> "$index:${it.reg}"
|
||||
it.variable -> "$index:variable"
|
||||
else -> "$index:???"
|
||||
}
|
||||
}
|
||||
val returnConv =
|
||||
when {
|
||||
returns.reg!=null -> returns.reg.toString()
|
||||
returns.floatFac1 -> "floatFAC1"
|
||||
else -> "<no returnvalue>"
|
||||
}
|
||||
return "CallConvention[" + paramConvs.joinToString() + " ; returns: $returnConv]"
|
||||
}
|
||||
}
|
||||
|
||||
class FParam(val name: String, val possibleDatatypes: Array<DataType>)
|
||||
|
||||
class FSignature(val pure: Boolean, // does it have side effects?
|
||||
val parameters: List<FParam>,
|
||||
val returnType: DataType?) {
|
||||
|
||||
fun callConvention(actualParamTypes: List<DataType>): CallConvention {
|
||||
val returns: ReturnConvention = when (returnType) {
|
||||
DataType.UBYTE, DataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A, false)
|
||||
DataType.UWORD, DataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY, false)
|
||||
DataType.FLOAT -> ReturnConvention(returnType, null, true)
|
||||
in PassByReferenceDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY, false)
|
||||
null -> ReturnConvention(null, null, false)
|
||||
else -> {
|
||||
// return type depends on arg type
|
||||
when (val paramType = actualParamTypes.first()) {
|
||||
DataType.UBYTE, DataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A, false)
|
||||
DataType.UWORD, DataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY, false)
|
||||
DataType.FLOAT -> ReturnConvention(paramType, null, true)
|
||||
in PassByReferenceDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY, false)
|
||||
else -> ReturnConvention(paramType, null, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return when {
|
||||
actualParamTypes.isEmpty() -> CallConvention(emptyList(), returns)
|
||||
actualParamTypes.size==1 -> {
|
||||
// one parameter goes via register/registerpair
|
||||
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)
|
||||
in PassByReferenceDatatypes -> ParamConvention(paramType, RegisterOrPair.AY, false)
|
||||
else -> ParamConvention(paramType, null, false)
|
||||
}
|
||||
CallConvention(listOf(paramConv), returns)
|
||||
}
|
||||
else -> {
|
||||
// multiple parameters go via variables
|
||||
val paramConvs = actualParamTypes.map { ParamConvention(it, null, true) }
|
||||
CallConvention(paramConvs, returns)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val BuiltinFunctions: Map<String, FSignature> = mapOf(
|
||||
// this set of function have no return value and operate in-place:
|
||||
"rol" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
"ror" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
"rol2" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
"ror2" to FSignature(false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
"sort" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||
"reverse" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||
// cmp returns a status in the carry flag, but not a proper return value
|
||||
"cmp" to FSignature(false, listOf(FParam("value1", IntegerDatatypesNoBool), FParam("value2", NumericDatatypesNoBool)), null),
|
||||
"abs" to FSignature(true, listOf(FParam("value", IntegerDatatypesNoBool)), DataType.UWORD),
|
||||
"len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD),
|
||||
// normal functions follow:
|
||||
"sizeof" to FSignature(true, listOf(FParam("object", DataType.values())), DataType.UBYTE),
|
||||
"sgn" to FSignature(true, listOf(FParam("value", NumericDatatypesNoBool)), DataType.BYTE),
|
||||
"sqrt16" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE),
|
||||
"any" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
|
||||
"all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
|
||||
"lsb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
|
||||
"msb" to FSignature(true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
|
||||
"mkword" to FSignature(true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD),
|
||||
"peek" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE),
|
||||
"peekw" to FSignature(true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||
"poke" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),
|
||||
"pokemon" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),
|
||||
"pokew" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UWORD))), null),
|
||||
"pop" to FSignature(false, listOf(FParam("target", ByteDatatypes)), null),
|
||||
"popw" to FSignature(false, listOf(FParam("target", WordDatatypes)), null),
|
||||
"push" to FSignature(false, listOf(FParam("value", ByteDatatypes)), null),
|
||||
"pushw" to FSignature(false, listOf(FParam("value", WordDatatypes)), null),
|
||||
"rsave" to FSignature(false, emptyList(), null),
|
||||
"rsavex" to FSignature(false, emptyList(), null),
|
||||
"rrestore" to FSignature(false, emptyList(), null),
|
||||
"rrestorex" to FSignature(false, emptyList(), null),
|
||||
"memory" to FSignature(true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||
"callfar" to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), null),
|
||||
"callrom" to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), null),
|
||||
)
|
||||
|
||||
val InplaceModifyingBuiltinFunctions = setOf("rol", "ror", "rol2", "ror2", "sort", "reverse")
|
@ -1,114 +0,0 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.core.*
|
||||
|
||||
class ReturnConvention(val dt: DataType?, val reg: RegisterOrPair?, val floatFac1: Boolean)
|
||||
class ParamConvention(val dt: DataType, 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 ->
|
||||
when {
|
||||
it.reg!=null -> "$index:${it.reg}"
|
||||
it.variable -> "$index:variable"
|
||||
else -> "$index:???"
|
||||
}
|
||||
}
|
||||
val returnConv =
|
||||
when {
|
||||
returns.reg!=null -> returns.reg.toString()
|
||||
returns.floatFac1 -> "floatFAC1"
|
||||
else -> "<no returnvalue>"
|
||||
}
|
||||
return "CallConvention[" + paramConvs.joinToString() + " ; returns: $returnConv]"
|
||||
}
|
||||
}
|
||||
|
||||
class FParam(val name: String, val possibleDatatypes: Array<DataType>)
|
||||
|
||||
class FSignature(val name: String,
|
||||
val pure: Boolean, // does it have side effects?
|
||||
val parameters: List<FParam>,
|
||||
val returnType: DataType?) {
|
||||
|
||||
fun callConvention(actualParamTypes: List<DataType>): CallConvention {
|
||||
val returns: ReturnConvention = when (returnType) {
|
||||
DataType.UBYTE, DataType.BYTE -> ReturnConvention(returnType, RegisterOrPair.A, false)
|
||||
DataType.UWORD, DataType.WORD -> ReturnConvention(returnType, RegisterOrPair.AY, false)
|
||||
DataType.FLOAT -> ReturnConvention(returnType, null, true)
|
||||
in PassByReferenceDatatypes -> ReturnConvention(returnType!!, RegisterOrPair.AY, false)
|
||||
null -> ReturnConvention(null, null, false)
|
||||
else -> {
|
||||
// return type depends on arg type
|
||||
when (val paramType = actualParamTypes.first()) {
|
||||
DataType.UBYTE, DataType.BYTE -> ReturnConvention(paramType, RegisterOrPair.A, false)
|
||||
DataType.UWORD, DataType.WORD -> ReturnConvention(paramType, RegisterOrPair.AY, false)
|
||||
DataType.FLOAT -> ReturnConvention(paramType, null, true)
|
||||
in PassByReferenceDatatypes -> ReturnConvention(paramType, RegisterOrPair.AY, false)
|
||||
else -> ReturnConvention(paramType, null, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return when {
|
||||
actualParamTypes.isEmpty() -> CallConvention(emptyList(), returns)
|
||||
actualParamTypes.size==1 -> {
|
||||
// one parameter goes via register/registerpair
|
||||
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)
|
||||
in PassByReferenceDatatypes -> ParamConvention(paramType, RegisterOrPair.AY, false)
|
||||
else -> ParamConvention(paramType, null, false)
|
||||
}
|
||||
CallConvention(listOf(paramConv), returns)
|
||||
}
|
||||
else -> {
|
||||
// multiple parameters go via variables
|
||||
val paramConvs = actualParamTypes.map { ParamConvention(it, null, true) }
|
||||
CallConvention(paramConvs, returns)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val functionSignatures: List<FSignature> = listOf(
|
||||
// this set of function have no return value and operate in-place:
|
||||
FSignature("rol" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
FSignature("ror" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
FSignature("rol2" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
FSignature("ror2" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
FSignature("sort" , false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||
FSignature("reverse" , false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||
// cmp returns a status in the carry flag, but not a proper return value
|
||||
FSignature("cmp" , false, listOf(FParam("value1", IntegerDatatypesNoBool), FParam("value2", NumericDatatypesNoBool)), null),
|
||||
FSignature("abs" , true, listOf(FParam("value", IntegerDatatypesNoBool)), DataType.UWORD),
|
||||
FSignature("len" , true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD),
|
||||
// normal functions follow:
|
||||
FSignature("sizeof" , true, listOf(FParam("object", DataType.values())), DataType.UBYTE),
|
||||
FSignature("sgn" , true, listOf(FParam("value", NumericDatatypesNoBool)), DataType.BYTE),
|
||||
FSignature("sqrt16" , true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE),
|
||||
FSignature("any" , true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
|
||||
FSignature("all" , true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE),
|
||||
FSignature("lsb" , true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
|
||||
FSignature("msb" , true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE),
|
||||
FSignature("mkword" , true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD),
|
||||
FSignature("peek" , true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE),
|
||||
FSignature("peekw" , true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||
FSignature("poke" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),
|
||||
FSignature("pokemon" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),
|
||||
FSignature("pokew" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UWORD))), null),
|
||||
FSignature("pop" , false, listOf(FParam("target", ByteDatatypes)), null),
|
||||
FSignature("popw" , false, listOf(FParam("target", WordDatatypes)), null),
|
||||
FSignature("push" , false, listOf(FParam("value", ByteDatatypes)), null),
|
||||
FSignature("pushw" , false, listOf(FParam("value", WordDatatypes)), null),
|
||||
FSignature("rsave" , false, emptyList(), null),
|
||||
FSignature("rsavex" , false, emptyList(), null),
|
||||
FSignature("rrestore" , false, emptyList(), null),
|
||||
FSignature("rrestorex" , false, emptyList(), null),
|
||||
FSignature("memory" , true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||
FSignature("callfar" , false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), null),
|
||||
FSignature("callrom" , false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), null),
|
||||
)
|
||||
|
||||
val BuiltinFunctions = functionSignatures.associateBy { it.name }
|
@ -318,7 +318,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
private fun funcMemory(fcall: PtBuiltinFunctionCall, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||
if(discardResult || fcall !is PtBuiltinFunctionCall)
|
||||
if(discardResult || fcall !is PtBuiltinFunctionCall) // TODO huh, is always this class??
|
||||
throw AssemblyError("should not discard result of memory allocation at $fcall")
|
||||
val name = (fcall.args[0] as PtString).value
|
||||
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name ${fcall.position}"}
|
||||
@ -1054,7 +1054,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
val value = it.first.first
|
||||
when {
|
||||
conv.variable -> {
|
||||
val varname = "prog8_lib.func_${signature.name}._arg_${paramName}"
|
||||
val varname = "prog8_lib.func_${call.name}._arg_${paramName}"
|
||||
val src = when (conv.dt) {
|
||||
DataType.FLOAT -> getSourceForFloat(value)
|
||||
in PassByReferenceDatatypes -> {
|
||||
|
@ -2,16 +2,15 @@ package prog8tests.codegencpu6502
|
||||
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.shouldBe
|
||||
import prog8.code.core.BuiltinFunctions
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.NumericDatatypesNoBool
|
||||
import prog8.code.core.RegisterOrPair
|
||||
import prog8.codegen.cpu6502.BuiltinFunctions
|
||||
|
||||
class TestBuiltinFunctions: FunSpec({
|
||||
|
||||
test("pure func with fixed type") {
|
||||
val func = BuiltinFunctions.getValue("sgn")
|
||||
func.name shouldBe "sgn"
|
||||
func.parameters.size shouldBe 1
|
||||
func.parameters[0].name shouldBe "value"
|
||||
func.parameters[0].possibleDatatypes shouldBe NumericDatatypesNoBool
|
||||
@ -30,7 +29,6 @@ class TestBuiltinFunctions: FunSpec({
|
||||
|
||||
test("not-pure func with varying result value type") {
|
||||
val func = BuiltinFunctions.getValue("cmp")
|
||||
func.name shouldBe "cmp"
|
||||
func.parameters.size shouldBe 2
|
||||
func.pure shouldBe false
|
||||
func.returnType shouldBe null
|
||||
@ -44,7 +42,6 @@ class TestBuiltinFunctions: FunSpec({
|
||||
|
||||
test("func without return type") {
|
||||
val func = BuiltinFunctions.getValue("poke")
|
||||
func.name shouldBe "poke"
|
||||
func.parameters.size shouldBe 2
|
||||
func.parameters[0].name shouldBe "address"
|
||||
func.parameters[0].possibleDatatypes shouldBe arrayOf(DataType.UWORD)
|
||||
|
@ -14,74 +14,38 @@ import kotlin.math.sqrt
|
||||
|
||||
private typealias ConstExpressionCaller = (args: List<Expression>, position: Position, program: Program) -> NumericLiteral
|
||||
|
||||
class FParam(val name: String, val possibleDatatypes: Array<DataType>)
|
||||
|
||||
class FSignature(val name: String,
|
||||
val pure: Boolean, // does it have side effects?
|
||||
val parameters: List<FParam>,
|
||||
val returnType: DataType?,
|
||||
val constExpressionFunc: ConstExpressionCaller? = null)
|
||||
|
||||
|
||||
private val functionSignatures: List<FSignature> = listOf(
|
||||
// this set of function have no return value and operate in-place:
|
||||
FSignature("rol" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
FSignature("ror" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
FSignature("rol2" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
FSignature("ror2" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||
FSignature("sort" , false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||
FSignature("reverse" , false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||
// cmp returns a status in the carry flag, but not a proper return value
|
||||
FSignature("cmp" , false, listOf(FParam("value1", IntegerDatatypesNoBool), FParam("value2", NumericDatatypesNoBool)), null),
|
||||
FSignature("abs" , true, listOf(FParam("value", IntegerDatatypesNoBool)), DataType.UWORD, ::builtinAbs),
|
||||
FSignature("len" , true, listOf(FParam("values", IterableDatatypes)), DataType.UWORD, ::builtinLen),
|
||||
// normal functions follow:
|
||||
FSignature("sizeof" , true, listOf(FParam("object", DataType.values())), DataType.UBYTE, ::builtinSizeof),
|
||||
FSignature("sgn" , true, listOf(FParam("value", NumericDatatypesNoBool)), DataType.BYTE, ::builtinSgn ),
|
||||
FSignature("sqrt16" , true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()) } },
|
||||
FSignature("any" , true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAny) },
|
||||
FSignature("all" , true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAll) },
|
||||
FSignature("lsb" , true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x and 255).toDouble() } },
|
||||
FSignature("msb" , true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x ushr 8 and 255).toDouble()} },
|
||||
FSignature("mkword" , true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD, ::builtinMkword),
|
||||
FSignature("peek" , true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE),
|
||||
FSignature("peekw" , true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||
FSignature("poke" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),
|
||||
FSignature("pokemon" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),
|
||||
FSignature("pokew" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UWORD))), null),
|
||||
FSignature("pop" , false, listOf(FParam("target", ByteDatatypes)), null),
|
||||
FSignature("popw" , false, listOf(FParam("target", WordDatatypes)), null),
|
||||
FSignature("push" , false, listOf(FParam("value", ByteDatatypes)), null),
|
||||
FSignature("pushw" , false, listOf(FParam("value", WordDatatypes)), null),
|
||||
FSignature("rsave" , false, emptyList(), null),
|
||||
FSignature("rsavex" , false, emptyList(), null),
|
||||
FSignature("rrestore" , false, emptyList(), null),
|
||||
FSignature("rrestorex" , false, emptyList(), null),
|
||||
FSignature("memory" , true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD),
|
||||
FSignature("callfar" , false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), null),
|
||||
FSignature("callrom" , false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), null),
|
||||
internal val constEvaluatorsForBuiltinFuncs: Map<String, ConstExpressionCaller> = mapOf(
|
||||
"abs" to ::builtinAbs,
|
||||
"len" to ::builtinLen,
|
||||
"sizeof" to ::builtinSizeof,
|
||||
"sgn" to ::builtinSgn,
|
||||
"sqrt16" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()) } },
|
||||
"any" to { a, p, prg -> collectionArg(a, p, prg, ::builtinAny) },
|
||||
"all" to { a, p, prg -> collectionArg(a, p, prg, ::builtinAll) },
|
||||
"lsb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x and 255).toDouble() } },
|
||||
"msb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x ushr 8 and 255).toDouble()} },
|
||||
"mkword" to ::builtinMkword
|
||||
)
|
||||
|
||||
val BuiltinFunctions = functionSignatures.associateBy { it.name }
|
||||
val InplaceModifyingBuiltinFunctions = setOf("rol", "ror", "rol2", "ror2", "sort", "reverse")
|
||||
|
||||
private fun builtinAny(array: List<Double>): Double = if(array.any { it!=0.0 }) 1.0 else 0.0
|
||||
|
||||
private fun builtinAll(array: List<Double>): Double = if(array.all { it!=0.0 }) 1.0 else 0.0
|
||||
|
||||
fun builtinFunctionReturnType(function: String): InferredTypes.InferredType {
|
||||
internal fun builtinFunctionReturnType(function: String): InferredTypes.InferredType {
|
||||
if(function in arrayOf("set_carry", "set_irqd", "clear_carry", "clear_irqd"))
|
||||
return InferredTypes.InferredType.void()
|
||||
|
||||
val func = BuiltinFunctions.getValue(function)
|
||||
if(func.returnType==null)
|
||||
return InferredTypes.InferredType.void()
|
||||
return InferredTypes.knownFor(func.returnType)
|
||||
val returnType = func.returnType
|
||||
return if(returnType==null)
|
||||
InferredTypes.InferredType.void()
|
||||
else
|
||||
InferredTypes.knownFor(returnType)
|
||||
}
|
||||
|
||||
|
||||
class NotConstArgumentException: AstException("not a const argument to a built-in function")
|
||||
class CannotEvaluateException(func:String, msg: String): FatalAstException("cannot evaluate built-in function $func: $msg")
|
||||
internal class NotConstArgumentException: AstException("not a const argument to a built-in function")
|
||||
internal class CannotEvaluateException(func:String, msg: String): FatalAstException("cannot evaluate built-in function $func: $msg")
|
||||
|
||||
|
||||
private fun oneIntArgOutputInt(args: List<Expression>, position: Position, program: Program, function: (arg: Int)->Double): NumericLiteral {
|
@ -208,7 +208,7 @@ private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuilt
|
||||
override fun constValue(funcName: String, args: List<Expression>, position: Position): NumericLiteral? {
|
||||
val func = BuiltinFunctions[funcName]
|
||||
if(func!=null) {
|
||||
val exprfunc = func.constExpressionFunc
|
||||
val exprfunc = constEvaluatorsForBuiltinFuncs[funcName]
|
||||
if(exprfunc!=null) {
|
||||
return try {
|
||||
exprfunc(args, position, program)
|
||||
|
@ -8,8 +8,6 @@ import prog8.ast.statements.*
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.VMTarget
|
||||
import prog8.compiler.BuiltinFunctions
|
||||
import prog8.compiler.InplaceModifyingBuiltinFunctions
|
||||
import prog8.compiler.builtinFunctionReturnType
|
||||
import java.io.CharConversionException
|
||||
import java.io.File
|
||||
|
@ -7,11 +7,11 @@ import prog8.ast.expressions.FunctionCallExpression
|
||||
import prog8.ast.expressions.StringLiteral
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
import prog8.code.core.BuiltinFunctions
|
||||
import prog8.code.core.ICompilationTarget
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.target.VMTarget
|
||||
import prog8.compiler.BuiltinFunctions
|
||||
|
||||
|
||||
internal class AstIdentifiersChecker(private val errors: IErrorReporter,
|
||||
@ -163,7 +163,7 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
|
||||
val pos = (if(call.args.any()) call.args[0] else (call as Node)).position
|
||||
errors.err("invalid number of arguments", pos)
|
||||
}
|
||||
if(func.name=="memory") {
|
||||
if(target.name=="memory") {
|
||||
val name = call.args[0] as? StringLiteral
|
||||
if(name!=null) {
|
||||
val processed = name.value.map {
|
||||
|
@ -10,11 +10,7 @@ import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.core.SourceCode
|
||||
import prog8.compiler.BuiltinFunctions
|
||||
import prog8.code.core.*
|
||||
import prog8.compiler.builtinFunctionReturnType
|
||||
import java.io.File
|
||||
import kotlin.io.path.Path
|
||||
|
@ -9,7 +9,6 @@ import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.*
|
||||
import prog8.compiler.BuiltinFunctions
|
||||
|
||||
|
||||
class TypecastsAdder(val program: Program, val options: CompilationOptions, val errors: IErrorReporter) : AstWalker() {
|
||||
|
@ -5,11 +5,7 @@ import prog8.ast.Program
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
import prog8.code.core.ByteDatatypes
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.core.Position
|
||||
import prog8.compiler.BuiltinFunctions
|
||||
import prog8.code.core.*
|
||||
|
||||
internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorReporter) : IAstVisitor {
|
||||
|
||||
|
@ -1,7 +1,10 @@
|
||||
package prog8tests
|
||||
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.shouldNotBe
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.ast.statements.Assignment
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8tests.helpers.compileText
|
||||
|
||||
@ -19,5 +22,31 @@ class TestBuiltinFunctions: FunSpec({
|
||||
}"""
|
||||
compileText(Cx16Target(), false, src, writeAssembly = true) shouldNotBe null
|
||||
}
|
||||
|
||||
test("certain builtin functions should be compile time evaluated") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
|
||||
uword[] array = [1,2,3]
|
||||
str name = "hello"
|
||||
cx16.r0L = len(array)
|
||||
cx16.r0L = len(name)
|
||||
cx16.r0L = sizeof(array)
|
||||
cx16.r0 = mkword(200,100)
|
||||
}
|
||||
}"""
|
||||
val result = compileText(Cx16Target(), false, src, writeAssembly = false)
|
||||
val statements = result!!.program.entrypoint.statements
|
||||
statements.size shouldBe 6
|
||||
val a1 = statements[2] as Assignment
|
||||
val a2 = statements[3] as Assignment
|
||||
val a3 = statements[4] as Assignment
|
||||
val a4 = statements[5] as Assignment
|
||||
(a1.value as NumericLiteral).number shouldBe 3.0
|
||||
(a2.value as NumericLiteral).number shouldBe 5.0
|
||||
(a3.value as NumericLiteral).number shouldBe 6.0
|
||||
(a4.value as NumericLiteral).number shouldBe 200*256+100
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -19,4 +19,5 @@ main {
|
||||
}"""
|
||||
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
|
||||
}
|
||||
|
||||
})
|
@ -10,7 +10,6 @@ import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
import prog8.code.core.*
|
||||
import prog8.compiler.BuiltinFunctions
|
||||
import java.util.*
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.floor
|
||||
|
Loading…
Reference in New Issue
Block a user