mirror of
https://github.com/irmen/prog8.git
synced 2024-10-17 10:24:55 +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?) {
|
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")
|
throw AssemblyError("should not discard result of memory allocation at $fcall")
|
||||||
val name = (fcall.args[0] as PtString).value
|
val name = (fcall.args[0] as PtString).value
|
||||||
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name ${fcall.position}"}
|
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
|
val value = it.first.first
|
||||||
when {
|
when {
|
||||||
conv.variable -> {
|
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) {
|
val src = when (conv.dt) {
|
||||||
DataType.FLOAT -> getSourceForFloat(value)
|
DataType.FLOAT -> getSourceForFloat(value)
|
||||||
in PassByReferenceDatatypes -> {
|
in PassByReferenceDatatypes -> {
|
||||||
|
@ -2,16 +2,15 @@ package prog8tests.codegencpu6502
|
|||||||
|
|
||||||
import io.kotest.core.spec.style.FunSpec
|
import io.kotest.core.spec.style.FunSpec
|
||||||
import io.kotest.matchers.shouldBe
|
import io.kotest.matchers.shouldBe
|
||||||
|
import prog8.code.core.BuiltinFunctions
|
||||||
import prog8.code.core.DataType
|
import prog8.code.core.DataType
|
||||||
import prog8.code.core.NumericDatatypesNoBool
|
import prog8.code.core.NumericDatatypesNoBool
|
||||||
import prog8.code.core.RegisterOrPair
|
import prog8.code.core.RegisterOrPair
|
||||||
import prog8.codegen.cpu6502.BuiltinFunctions
|
|
||||||
|
|
||||||
class TestBuiltinFunctions: FunSpec({
|
class TestBuiltinFunctions: FunSpec({
|
||||||
|
|
||||||
test("pure func with fixed type") {
|
test("pure func with fixed type") {
|
||||||
val func = BuiltinFunctions.getValue("sgn")
|
val func = BuiltinFunctions.getValue("sgn")
|
||||||
func.name shouldBe "sgn"
|
|
||||||
func.parameters.size shouldBe 1
|
func.parameters.size shouldBe 1
|
||||||
func.parameters[0].name shouldBe "value"
|
func.parameters[0].name shouldBe "value"
|
||||||
func.parameters[0].possibleDatatypes shouldBe NumericDatatypesNoBool
|
func.parameters[0].possibleDatatypes shouldBe NumericDatatypesNoBool
|
||||||
@ -30,7 +29,6 @@ class TestBuiltinFunctions: FunSpec({
|
|||||||
|
|
||||||
test("not-pure func with varying result value type") {
|
test("not-pure func with varying result value type") {
|
||||||
val func = BuiltinFunctions.getValue("cmp")
|
val func = BuiltinFunctions.getValue("cmp")
|
||||||
func.name shouldBe "cmp"
|
|
||||||
func.parameters.size shouldBe 2
|
func.parameters.size shouldBe 2
|
||||||
func.pure shouldBe false
|
func.pure shouldBe false
|
||||||
func.returnType shouldBe null
|
func.returnType shouldBe null
|
||||||
@ -44,7 +42,6 @@ class TestBuiltinFunctions: FunSpec({
|
|||||||
|
|
||||||
test("func without return type") {
|
test("func without return type") {
|
||||||
val func = BuiltinFunctions.getValue("poke")
|
val func = BuiltinFunctions.getValue("poke")
|
||||||
func.name shouldBe "poke"
|
|
||||||
func.parameters.size shouldBe 2
|
func.parameters.size shouldBe 2
|
||||||
func.parameters[0].name shouldBe "address"
|
func.parameters[0].name shouldBe "address"
|
||||||
func.parameters[0].possibleDatatypes shouldBe arrayOf(DataType.UWORD)
|
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
|
private typealias ConstExpressionCaller = (args: List<Expression>, position: Position, program: Program) -> NumericLiteral
|
||||||
|
|
||||||
class FParam(val name: String, val possibleDatatypes: Array<DataType>)
|
internal val constEvaluatorsForBuiltinFuncs: Map<String, ConstExpressionCaller> = mapOf(
|
||||||
|
"abs" to ::builtinAbs,
|
||||||
class FSignature(val name: String,
|
"len" to ::builtinLen,
|
||||||
val pure: Boolean, // does it have side effects?
|
"sizeof" to ::builtinSizeof,
|
||||||
val parameters: List<FParam>,
|
"sgn" to ::builtinSgn,
|
||||||
val returnType: DataType?,
|
"sqrt16" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()) } },
|
||||||
val constExpressionFunc: ConstExpressionCaller? = null)
|
"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() } },
|
||||||
private val functionSignatures: List<FSignature> = listOf(
|
"msb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> (x ushr 8 and 255).toDouble()} },
|
||||||
// this set of function have no return value and operate in-place:
|
"mkword" to ::builtinMkword
|
||||||
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),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
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 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
|
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"))
|
if(function in arrayOf("set_carry", "set_irqd", "clear_carry", "clear_irqd"))
|
||||||
return InferredTypes.InferredType.void()
|
return InferredTypes.InferredType.void()
|
||||||
|
|
||||||
val func = BuiltinFunctions.getValue(function)
|
val func = BuiltinFunctions.getValue(function)
|
||||||
if(func.returnType==null)
|
val returnType = func.returnType
|
||||||
return InferredTypes.InferredType.void()
|
return if(returnType==null)
|
||||||
return InferredTypes.knownFor(func.returnType)
|
InferredTypes.InferredType.void()
|
||||||
|
else
|
||||||
|
InferredTypes.knownFor(returnType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class NotConstArgumentException: AstException("not a const argument to a built-in function")
|
internal 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 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 {
|
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? {
|
override fun constValue(funcName: String, args: List<Expression>, position: Position): NumericLiteral? {
|
||||||
val func = BuiltinFunctions[funcName]
|
val func = BuiltinFunctions[funcName]
|
||||||
if(func!=null) {
|
if(func!=null) {
|
||||||
val exprfunc = func.constExpressionFunc
|
val exprfunc = constEvaluatorsForBuiltinFuncs[funcName]
|
||||||
if(exprfunc!=null) {
|
if(exprfunc!=null) {
|
||||||
return try {
|
return try {
|
||||||
exprfunc(args, position, program)
|
exprfunc(args, position, program)
|
||||||
|
@ -8,8 +8,6 @@ import prog8.ast.statements.*
|
|||||||
import prog8.ast.walk.IAstVisitor
|
import prog8.ast.walk.IAstVisitor
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.code.target.VMTarget
|
import prog8.code.target.VMTarget
|
||||||
import prog8.compiler.BuiltinFunctions
|
|
||||||
import prog8.compiler.InplaceModifyingBuiltinFunctions
|
|
||||||
import prog8.compiler.builtinFunctionReturnType
|
import prog8.compiler.builtinFunctionReturnType
|
||||||
import java.io.CharConversionException
|
import java.io.CharConversionException
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
@ -7,11 +7,11 @@ import prog8.ast.expressions.FunctionCallExpression
|
|||||||
import prog8.ast.expressions.StringLiteral
|
import prog8.ast.expressions.StringLiteral
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.IAstVisitor
|
import prog8.ast.walk.IAstVisitor
|
||||||
|
import prog8.code.core.BuiltinFunctions
|
||||||
import prog8.code.core.ICompilationTarget
|
import prog8.code.core.ICompilationTarget
|
||||||
import prog8.code.core.IErrorReporter
|
import prog8.code.core.IErrorReporter
|
||||||
import prog8.code.core.Position
|
import prog8.code.core.Position
|
||||||
import prog8.code.target.VMTarget
|
import prog8.code.target.VMTarget
|
||||||
import prog8.compiler.BuiltinFunctions
|
|
||||||
|
|
||||||
|
|
||||||
internal class AstIdentifiersChecker(private val errors: IErrorReporter,
|
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
|
val pos = (if(call.args.any()) call.args[0] else (call as Node)).position
|
||||||
errors.err("invalid number of arguments", pos)
|
errors.err("invalid number of arguments", pos)
|
||||||
}
|
}
|
||||||
if(func.name=="memory") {
|
if(target.name=="memory") {
|
||||||
val name = call.args[0] as? StringLiteral
|
val name = call.args[0] as? StringLiteral
|
||||||
if(name!=null) {
|
if(name!=null) {
|
||||||
val processed = name.value.map {
|
val processed = name.value.map {
|
||||||
|
@ -10,11 +10,7 @@ import prog8.ast.expressions.*
|
|||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.code.SymbolTable
|
import prog8.code.SymbolTable
|
||||||
import prog8.code.ast.*
|
import prog8.code.ast.*
|
||||||
import prog8.code.core.CompilationOptions
|
import prog8.code.core.*
|
||||||
import prog8.code.core.DataType
|
|
||||||
import prog8.code.core.Position
|
|
||||||
import prog8.code.core.SourceCode
|
|
||||||
import prog8.compiler.BuiltinFunctions
|
|
||||||
import prog8.compiler.builtinFunctionReturnType
|
import prog8.compiler.builtinFunctionReturnType
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.io.path.Path
|
import kotlin.io.path.Path
|
||||||
|
@ -9,7 +9,6 @@ import prog8.ast.statements.*
|
|||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.compiler.BuiltinFunctions
|
|
||||||
|
|
||||||
|
|
||||||
class TypecastsAdder(val program: Program, val options: CompilationOptions, val errors: IErrorReporter) : AstWalker() {
|
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.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.IAstVisitor
|
import prog8.ast.walk.IAstVisitor
|
||||||
import prog8.code.core.ByteDatatypes
|
import prog8.code.core.*
|
||||||
import prog8.code.core.DataType
|
|
||||||
import prog8.code.core.IErrorReporter
|
|
||||||
import prog8.code.core.Position
|
|
||||||
import prog8.compiler.BuiltinFunctions
|
|
||||||
|
|
||||||
internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorReporter) : IAstVisitor {
|
internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorReporter) : IAstVisitor {
|
||||||
|
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package prog8tests
|
package prog8tests
|
||||||
|
|
||||||
import io.kotest.core.spec.style.FunSpec
|
import io.kotest.core.spec.style.FunSpec
|
||||||
|
import io.kotest.matchers.shouldBe
|
||||||
import io.kotest.matchers.shouldNotBe
|
import io.kotest.matchers.shouldNotBe
|
||||||
|
import prog8.ast.expressions.NumericLiteral
|
||||||
|
import prog8.ast.statements.Assignment
|
||||||
import prog8.code.target.Cx16Target
|
import prog8.code.target.Cx16Target
|
||||||
import prog8tests.helpers.compileText
|
import prog8tests.helpers.compileText
|
||||||
|
|
||||||
@ -19,5 +22,31 @@ class TestBuiltinFunctions: FunSpec({
|
|||||||
}"""
|
}"""
|
||||||
compileText(Cx16Target(), false, src, writeAssembly = true) shouldNotBe null
|
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
|
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.AstWalker
|
||||||
import prog8.ast.walk.IAstVisitor
|
import prog8.ast.walk.IAstVisitor
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
import prog8.compiler.BuiltinFunctions
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
|
Loading…
Reference in New Issue
Block a user