From 117d8484663a423c427bb186333d4c902acf6136 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 24 Jan 2023 23:59:53 +0100 Subject: [PATCH] consolidate builtin function definitions into codeCore --- .../src/prog8/code/core/BuiltinFunctions.kt | 111 +++++++++++++++++ .../prog8/codegen/cpu6502/BuiltinFunctions.kt | 114 ------------------ .../codegen/cpu6502/BuiltinFunctionsAsmGen.kt | 4 +- codeGenCpu6502/test/TestBuiltinFunctions.kt | 5 +- .../src/prog8/compiler/BuiltinFunctions.kt | 74 +++--------- compiler/src/prog8/compiler/Compiler.kt | 2 +- .../compiler/astprocessing/AstChecker.kt | 2 - .../astprocessing/AstIdentifiersChecker.kt | 4 +- .../astprocessing/IntermediateAstMaker.kt | 6 +- .../compiler/astprocessing/TypecastsAdder.kt | 1 - .../astprocessing/VerifyFunctionArgTypes.kt | 6 +- compiler/test/TestBuiltinFunctions.kt | 29 +++++ compiler/test/codegeneration/TestVarious.kt | 1 + .../prog8/ast/expressions/AstExpressions.kt | 1 - 14 files changed, 168 insertions(+), 192 deletions(-) create mode 100644 codeCore/src/prog8/code/core/BuiltinFunctions.kt delete mode 100644 codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctions.kt rename {compilerAst => compiler}/src/prog8/compiler/BuiltinFunctions.kt (55%) diff --git a/codeCore/src/prog8/code/core/BuiltinFunctions.kt b/codeCore/src/prog8/code/core/BuiltinFunctions.kt new file mode 100644 index 000000000..1f924634e --- /dev/null +++ b/codeCore/src/prog8/code/core/BuiltinFunctions.kt @@ -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, 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 -> "" + } + return "CallConvention[" + paramConvs.joinToString() + " ; returns: $returnConv]" + } +} + +class FParam(val name: String, val possibleDatatypes: Array) + +class FSignature(val pure: Boolean, // does it have side effects? + val parameters: List, + val returnType: DataType?) { + + fun callConvention(actualParamTypes: List): 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 = 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") diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctions.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctions.kt deleted file mode 100644 index 362898f4c..000000000 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctions.kt +++ /dev/null @@ -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, 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 -> "" - } - return "CallConvention[" + paramConvs.joinToString() + " ; returns: $returnConv]" - } -} - -class FParam(val name: String, val possibleDatatypes: Array) - -class FSignature(val name: String, - val pure: Boolean, // does it have side effects? - val parameters: List, - val returnType: DataType?) { - - fun callConvention(actualParamTypes: List): 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 = 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 } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt index b08993458..3d0889337 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt @@ -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 -> { diff --git a/codeGenCpu6502/test/TestBuiltinFunctions.kt b/codeGenCpu6502/test/TestBuiltinFunctions.kt index a20f46ba7..500816c00 100644 --- a/codeGenCpu6502/test/TestBuiltinFunctions.kt +++ b/codeGenCpu6502/test/TestBuiltinFunctions.kt @@ -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) diff --git a/compilerAst/src/prog8/compiler/BuiltinFunctions.kt b/compiler/src/prog8/compiler/BuiltinFunctions.kt similarity index 55% rename from compilerAst/src/prog8/compiler/BuiltinFunctions.kt rename to compiler/src/prog8/compiler/BuiltinFunctions.kt index 0d1f6ed26..1c80b7889 100644 --- a/compilerAst/src/prog8/compiler/BuiltinFunctions.kt +++ b/compiler/src/prog8/compiler/BuiltinFunctions.kt @@ -14,74 +14,38 @@ import kotlin.math.sqrt private typealias ConstExpressionCaller = (args: List, position: Position, program: Program) -> NumericLiteral -class FParam(val name: String, val possibleDatatypes: Array) - -class FSignature(val name: String, - val pure: Boolean, // does it have side effects? - val parameters: List, - val returnType: DataType?, - val constExpressionFunc: ConstExpressionCaller? = null) - - -private val functionSignatures: List = 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 = 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 = if(array.any { it!=0.0 }) 1.0 else 0.0 private fun builtinAll(array: List): 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, position: Position, program: Program, function: (arg: Int)->Double): NumericLiteral { diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 84cc673d4..8a884819b 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -208,7 +208,7 @@ private class BuiltinFunctionsFacade(functions: Map): IBuilt override fun constValue(funcName: String, args: List, 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) diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index a084d939a..cc72440e9 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -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 diff --git a/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt index bf1254f1e..cd148516c 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt @@ -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 { diff --git a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt index 31f8f2cdc..315c328a7 100644 --- a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt +++ b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt @@ -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 diff --git a/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt b/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt index fa03fab27..83a06efa7 100644 --- a/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt +++ b/compiler/src/prog8/compiler/astprocessing/TypecastsAdder.kt @@ -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() { diff --git a/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt b/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt index 2fa6a9f12..b94e14266 100644 --- a/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt +++ b/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt @@ -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 { diff --git a/compiler/test/TestBuiltinFunctions.kt b/compiler/test/TestBuiltinFunctions.kt index 59e193afc..d835e3f21 100644 --- a/compiler/test/TestBuiltinFunctions.kt +++ b/compiler/test/TestBuiltinFunctions.kt @@ -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 + } }) diff --git a/compiler/test/codegeneration/TestVarious.kt b/compiler/test/codegeneration/TestVarious.kt index 785d2f066..cb3adc2dc 100644 --- a/compiler/test/codegeneration/TestVarious.kt +++ b/compiler/test/codegeneration/TestVarious.kt @@ -19,4 +19,5 @@ main { }""" compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null } + }) \ No newline at end of file diff --git a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt index c6b3a862e..774316f09 100644 --- a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt +++ b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt @@ -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