From edfd9d55ba546d5ae7c78d2227650a202a190b17 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Thu, 20 Aug 2020 13:50:28 +0200 Subject: [PATCH] added sizeof() function --- compiler/src/prog8/ast/base/Base.kt | 2 +- .../prog8/ast/expressions/AstExpressions.kt | 9 +-- .../compiler/target/IMachineDefinition.kt | 1 + .../target/c64/C64MachineDefinition.kt | 1 + .../src/prog8/functions/BuiltinFunctions.kt | 49 +++++++++++-- docs/source/programming.rst | 8 +- examples/test.p8 | 73 ++++++++++--------- 7 files changed, 92 insertions(+), 51 deletions(-) diff --git a/compiler/src/prog8/ast/base/Base.kt b/compiler/src/prog8/ast/base/Base.kt index 3f018818c..acb5a4b9a 100644 --- a/compiler/src/prog8/ast/base/Base.kt +++ b/compiler/src/prog8/ast/base/Base.kt @@ -58,7 +58,7 @@ enum class DataType { in ByteDatatypes -> 1 in WordDatatypes -> 2 FLOAT -> CompilationTarget.machine.FLOAT_MEM_SIZE - in PassByReferenceDatatypes -> 2 + in PassByReferenceDatatypes -> CompilationTarget.machine.POINTER_MEM_SIZE else -> -9999999 } } diff --git a/compiler/src/prog8/ast/expressions/AstExpressions.kt b/compiler/src/prog8/ast/expressions/AstExpressions.kt index 46b1362ef..1f48adc9a 100644 --- a/compiler/src/prog8/ast/expressions/AstExpressions.kt +++ b/compiler/src/prog8/ast/expressions/AstExpressions.kt @@ -716,11 +716,10 @@ data class IdentifierReference(val nameInSource: List, override val posi override fun referencesIdentifiers(vararg name: String): Boolean = nameInSource.last() in name override fun inferType(program: Program): InferredTypes.InferredType { - val targetStmt = targetStatement(program.namespace) - return if(targetStmt is VarDecl) { - InferredTypes.knownFor(targetStmt.datatype) - } else { - InferredTypes.InferredType.unknown() + return when (val targetStmt = targetStatement(program.namespace)) { + is VarDecl -> InferredTypes.knownFor(targetStmt.datatype) + is StructDecl -> InferredTypes.knownFor(DataType.STRUCT) + else -> InferredTypes.InferredType.unknown() } } diff --git a/compiler/src/prog8/compiler/target/IMachineDefinition.kt b/compiler/src/prog8/compiler/target/IMachineDefinition.kt index ef338dece..94e10324b 100644 --- a/compiler/src/prog8/compiler/target/IMachineDefinition.kt +++ b/compiler/src/prog8/compiler/target/IMachineDefinition.kt @@ -8,6 +8,7 @@ interface IMachineDefinition { val FLOAT_MAX_NEGATIVE: Double val FLOAT_MAX_POSITIVE: Double val FLOAT_MEM_SIZE: Int + val POINTER_MEM_SIZE: Int val opcodeNames: Set diff --git a/compiler/src/prog8/compiler/target/c64/C64MachineDefinition.kt b/compiler/src/prog8/compiler/target/c64/C64MachineDefinition.kt index 7b4eb20aa..cf3c3023f 100644 --- a/compiler/src/prog8/compiler/target/c64/C64MachineDefinition.kt +++ b/compiler/src/prog8/compiler/target/c64/C64MachineDefinition.kt @@ -14,6 +14,7 @@ object C64MachineDefinition: IMachineDefinition { override val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255 override val FLOAT_MAX_NEGATIVE = -1.7014118345e+38 // bytes: 255,255,255,255,255 override val FLOAT_MEM_SIZE = 5 + override val POINTER_MEM_SIZE = 2 const val BASIC_LOAD_ADDRESS = 0x0801 const val RAW_LOAD_ADDRESS = 0xc000 diff --git a/compiler/src/prog8/functions/BuiltinFunctions.kt b/compiler/src/prog8/functions/BuiltinFunctions.kt index 764c4c7ed..c59aae4b0 100644 --- a/compiler/src/prog8/functions/BuiltinFunctions.kt +++ b/compiler/src/prog8/functions/BuiltinFunctions.kt @@ -3,6 +3,8 @@ package prog8.functions import prog8.ast.Program import prog8.ast.base.* import prog8.ast.expressions.* +import prog8.ast.statements.StructDecl +import prog8.ast.statements.VarDecl import prog8.compiler.CompilerException import kotlin.math.* @@ -35,6 +37,7 @@ val BuiltinFunctions = mapOf( "sum" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinSum) }, // type depends on args "abs" to FSignature(true, listOf(FParam("value", NumericDatatypes)), null, ::builtinAbs), // type depends on argument "len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), null, ::builtinLen), // type is UBYTE or UWORD depending on actual length + "sizeof" to FSignature(true, listOf(FParam("object", DataType.values().toSet())), DataType.UBYTE, ::builtinSizeof), // normal functions follow: "sgn" to FSignature(true, listOf(FParam("value", NumericDatatypes)), DataType.BYTE, ::builtinSgn ), "sin" to FSignature(true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sin) }, @@ -240,6 +243,42 @@ private fun builtinAbs(args: List, position: Position, program: Prog } } +private fun builtinSizeof(args: List, position: Position, program: Program): NumericLiteralValue { + // 1 arg, type = anything, result type = ubyte + if(args.size!=1) + throw SyntaxError("sizeof requires one argument", position) + if(args[0] !is IdentifierReference) + throw SyntaxError("sizeof argument should be an identifier", position) + + val dt = args[0].inferType(program) + if(dt.isKnown) { + val target = (args[0] as IdentifierReference).targetStatement(program.namespace) + ?: throw CannotEvaluateException("sizeof", "no target") + + fun structSize(target: StructDecl) = + NumericLiteralValue(DataType.UBYTE, target.statements.map { (it as VarDecl).datatype.memorySize() }.sum(), position) + + return when { + dt.typeOrElse(DataType.STRUCT) in ArrayDatatypes -> { + val length = (target as VarDecl).arraysize!!.size() ?: throw CannotEvaluateException("sizeof", "unknown array size") + val elementDt = ArrayElementTypes.getValue(dt.typeOrElse(DataType.STRUCT)) + numericLiteral(elementDt.memorySize() * length, position) + } + dt.istype(DataType.STRUCT) -> { + when (target) { + is VarDecl -> structSize(target.struct!!) + is StructDecl -> structSize(target) + else -> throw CompilerException("weird struct type $target") + } + } + dt.istype(DataType.STR) -> throw SyntaxError("sizeof str is undefined, did you mean len?", position) + else -> NumericLiteralValue(DataType.UBYTE, dt.typeOrElse(DataType.STRUCT).memorySize(), position) + } + } else { + throw SyntaxError("sizeof invalid argument type", position) + } +} + private fun builtinStrlen(args: List, position: Position, program: Program): NumericLiteralValue { if (args.size != 1) throw SyntaxError("strlen requires one argument", position) @@ -262,18 +301,12 @@ private fun builtinLen(args: List, position: Position, program: Prog if(args[0] is ArrayLiteralValue) return NumericLiteralValue.optimalInteger((args[0] as ArrayLiteralValue).value.size, position) if(args[0] !is IdentifierReference) - throw SyntaxError("len argument should be an identifier, but is ${args[0]}", position) + throw SyntaxError("len argument should be an identifier", position) val target = (args[0] as IdentifierReference).targetVarDecl(program.namespace) ?: throw CannotEvaluateException("len", "no target vardecl") return when(target.datatype) { - DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> { - arraySize = target.arraysize?.size() - if(arraySize==null) - throw CannotEvaluateException("len", "arraysize unknown") - NumericLiteralValue.optimalInteger(arraySize, args[0].position) - } - DataType.ARRAY_F -> { + DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F -> { arraySize = target.arraysize?.size() if(arraySize==null) throw CannotEvaluateException("len", "arraysize unknown") diff --git a/docs/source/programming.rst b/docs/source/programming.rst index ac610542f..c27193b9d 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -724,11 +724,17 @@ reverse(array) len(x) Number of values in the array value x, or the number of characters in a string (excluding the size or 0-byte). - Note: this can be different from the number of *bytes* in memory if the datatype isn't a byte. + Note: this can be different from the number of *bytes* in memory if the datatype isn't a byte. See sizeof(). Note: lengths of strings and arrays are determined at compile-time! If your program modifies the actual length of the string during execution, the value of len(string) may no longer be correct! (use strlen function if you want to dynamically determine the length) +sizeof(name) + Number of bytes that the object 'name' occupies in memory. This is a constant determined by the data type of + the object. For instance, for a variable of type uword, the sizeof is 2. + For an 10 element array of floats, it is 50 (on the C-64, where a float is 5 bytes). + Note: usually you will be interested in the number of elements in an array, use len() for that. + strlen(str) Number of bytes in the string. This value is determined during runtime and counts upto the first terminating 0 byte in the string, regardless of the size of the string during compilation time. diff --git a/examples/test.p8 b/examples/test.p8 index 2b5e70fca..9b380bbc7 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -15,44 +15,45 @@ main { Color c = [11,22222,3.1234] -; c64scr.print_ub(c.red) -; c64.CHROUT('\n') -; c64scr.print_uw(c.green) -; c64.CHROUT('\n') -; c64flt.print_f(c.blue) -; c64.CHROUT('\n') + str string = "irmen" + byte[] ab = [1,2,3] + ubyte[] aub = [1,2,3] + word[] aw = [11,22,33] + uword[] auw = [11,22,33] + float[] af = [1.1,2.2,3.3] - uword xx = 4.5678 - ubyte bb = 33 - float ff = 1.234 + c64scr.print_ub(sizeof(ab)) + c64.CHROUT('\n') + c64scr.print_ub(sizeof(aub)) + c64.CHROUT('\n') + c64scr.print_ub(sizeof(aw)) + c64.CHROUT('\n') + c64scr.print_ub(sizeof(auw)) + c64.CHROUT('\n') + c64scr.print_ub(sizeof(af)) + c64.CHROUT('\n') + c64.CHROUT('\n') + c64.CHROUT('\n') - foo(1.234, 4.456) ; TODO truncation warning - foo2(2.3456) ; TODO truncation warning - foo2(bb) - foo2(4.55) ; TODO truncation warning + c64scr.print_ub(c.red) + c64.CHROUT('\n') + c64scr.print_uw(c.green) + c64.CHROUT('\n') + c64flt.print_f(c.blue) + c64.CHROUT('\n') - ;foo("zzz", 8.777) - ;len(13) - -; uword size = len(Color) -; c64scr.print_uw(size) -; c64.CHROUT('\n') - -; c64scr.print_ub(len(Color)) -; c64.CHROUT('\n') -; c64scr.print_ub(len(c)) -; c64.CHROUT('\n') -; c64scr.print_ub(len(c.green)) -; c64.CHROUT('\n') - } - - sub foo(ubyte aa, word ww) { - ww += aa - } - - asmsub foo2(ubyte aa @Pc) { - %asm {{ - rts - }} + ubyte size = sizeof(Color) + c64scr.print_ub(size) + c64.CHROUT('\n') + c64scr.print_ub(sizeof(Color)) + c64.CHROUT('\n') + c64scr.print_ub(sizeof(c)) + c64.CHROUT('\n') + c64scr.print_ub(sizeof(c.red)) + c64.CHROUT('\n') + c64scr.print_ub(sizeof(c.green)) + c64.CHROUT('\n') + c64scr.print_ub(sizeof(c.blue)) + c64.CHROUT('\n') } }