diff --git a/compiler/examples/test.p8 b/compiler/examples/test.p8 index dd94470a4..852a58795 100644 --- a/compiler/examples/test.p8 +++ b/compiler/examples/test.p8 @@ -17,24 +17,36 @@ float f2 float f3 - - b2 = 99 - w2 = -9999 - ub2 = 100 - uw2 = 40000 - f2 = 3.141592654 + ubyte[3] uba = [1,2,3] + byte[3] ba = [-1,2,3] + uword[3] uwa = [1000,2000,3000] + word[3] wa = -222 + ;word[3] wa = [-1000.w,2000.w,3000.w] ; @todo array data type fix (float->word) + ;word[3] wa = [1000,2000,3000] ; @todo array data type fix (uword->word) + float[3] fa = [-1000,44.555, 99.999] + str string = "hello" + str_p pstring = "hello1" + str_s sstring = "hello12" + str_ps psstring = "hello123" c64.CHROUT('x') c64scr.print_ubyte_decimal(X) c64.CHROUT('\n') - f1 = deg(f2) - c64flt.print_float(f1) + + ub1 = any(uba) + c64scr.print_ubyte_decimal(ub1) c64.CHROUT('\n') - c64.CHROUT('x') - c64scr.print_ubyte_decimal(X) + ub1 = any(ba) + c64scr.print_ubyte_decimal(ub1) c64.CHROUT('\n') - f1 = rad(f1) - c64flt.print_float(f1) + ub1 = any(uwa) + c64scr.print_ubyte_decimal(ub1) + c64.CHROUT('\n') + ub1 = any(wa) + c64scr.print_ubyte_decimal(ub1) + c64.CHROUT('\n') + ub1 = any(fa) + c64scr.print_ubyte_decimal(ub1) c64.CHROUT('\n') c64.CHROUT('x') c64scr.print_ubyte_decimal(X) diff --git a/compiler/src/prog8/ast/AST.kt b/compiler/src/prog8/ast/AST.kt index e513114a0..cc5b4b524 100644 --- a/compiler/src/prog8/ast/AST.kt +++ b/compiler/src/prog8/ast/AST.kt @@ -88,7 +88,8 @@ enum class BranchCondition { } val IterableDatatypes = setOf( - DataType.STR, DataType.STR_S, // note: the STR_P/STR_PS types aren't iterable because they store their length as the first byte + DataType.STR, DataType.STR_S, + DataType.STR_P, DataType.STR_PS, // note: these are a bit weird they store their length as the first byte DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F) @@ -1394,6 +1395,18 @@ class FunctionCall(override var target: IdentifierReference, private fun constValue(namespace: INameScope, heap: HeapValues, withDatatypeCheck: Boolean): LiteralValue? { // if the function is a built-in function and the args are consts, should try to const-evaluate! if(target.nameInSource.size>1) return null + if(target.nameInSource[0]=="len" && arglist.size==1) { + val arg=arglist[0] + if(arg is IdentifierReference) { + val target=arg.targetStatement(namespace) + if(target!=null) { + if (arg.resultingDatatype(namespace, heap) in StringDatatypes) { + // len on strings should be dynamic, all other cases are a compile-time constant + return null + } + } + } + } try { var resultValue: LiteralValue? = null val func = BuiltinFunctions[target.nameInSource[0]] diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index dd5333f24..5ac470b3f 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -716,8 +716,9 @@ class AstChecker(private val namespace: INameScope, checkResult.add(SyntaxError("invalid number of arguments", position)) else { for (arg in args.withIndex().zip(func.parameters)) { - if(arg.first.value.resultingDatatype(namespace, heap) !in arg.second.possibleDatatypes) - checkResult.add(ExpressionError("builtin function argument ${arg.first.index+1} has invalid type, expected ${arg.second.possibleDatatypes}", position)) + val argDt=arg.first.value.resultingDatatype(namespace, heap) + if(argDt !in arg.second.possibleDatatypes) + checkResult.add(ExpressionError("builtin function argument ${arg.first.index+1} has invalid type $argDt, expected ${arg.second.possibleDatatypes}", position)) } } } else if(target is Subroutine) { diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 418e193bf..4600c0442 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -671,6 +671,17 @@ private class StatementTranslator(private val prog: IntermediateProgram, // some functions are implemented as vm opcodes args.forEach { translate(it) } // place function argument(s) on the stack when (funcname) { + "len" -> { + // 1 argument, type determines the exact syscall to use + val arg=args.single() + when (arg.resultingDatatype(namespace, heap)) { + DataType.STR -> createSyscall("${funcname}_str") + DataType.STR_P -> createSyscall("${funcname}_strp") + DataType.STR_S -> createSyscall("${funcname}_str") + DataType.STR_PS -> createSyscall("${funcname}_strp") + else -> throw CompilerException("wrong datatype for len()") + } + } "flt" -> { // 1 argument, type determines the exact opcode to use val arg = args.single() diff --git a/compiler/src/prog8/functions/BuiltinFunctions.kt b/compiler/src/prog8/functions/BuiltinFunctions.kt index dbdb592ef..b1e444b58 100644 --- a/compiler/src/prog8/functions/BuiltinFunctions.kt +++ b/compiler/src/prog8/functions/BuiltinFunctions.kt @@ -1,6 +1,7 @@ package prog8.functions import prog8.ast.* +import prog8.compiler.CompilerException import prog8.compiler.HeapValues import kotlin.math.log2 diff --git a/compiler/src/prog8/stackvm/StackVm.kt b/compiler/src/prog8/stackvm/StackVm.kt index e387e444f..35dbc368d 100644 --- a/compiler/src/prog8/stackvm/StackVm.kt +++ b/compiler/src/prog8/stackvm/StackVm.kt @@ -46,7 +46,6 @@ enum class Syscall(val callNr: Short) { FUNC_MIN(83), FUNC_AVG(84), FUNC_SUM(85), - FUNC_LEN(86), FUNC_ANY(87), FUNC_ALL(88), FUNC_RND(89), // push a random byte on the stack @@ -58,7 +57,11 @@ enum class Syscall(val callNr: Short) { FUNC_STR2UBYTE(101), FUNC_STR2WORD(102), FUNC_STR2UWORD(103), - FUNC_STR2FLOAT(104) + FUNC_STR2FLOAT(104), + FUNC_LEN_STR(105), + FUNC_LEN_STRP(106), + FUNC_LEN_STRS(107), + FUNC_LEN_STRPS(108) // note: not all builtin functions of the Prog8 language are present as functions: // some of them are straight opcodes (such as MSB, LSB, LSL, LSR, ROL_BYTE, ROR, ROL2, ROR2, and FLT)! @@ -1454,7 +1457,11 @@ class StackVm(private var traceOutputFile: String?) { Syscall.FUNC_RND -> evalstack.push(Value(DataType.UBYTE, rnd.nextInt() and 255)) Syscall.FUNC_RNDW -> evalstack.push(Value(DataType.UWORD, rnd.nextInt() and 65535)) Syscall.FUNC_RNDF -> evalstack.push(Value(DataType.FLOAT, rnd.nextDouble())) - Syscall.FUNC_LEN -> throw VmExecutionException("len() should have been const-folded away everywhere (it's not possible on non-const values)") + Syscall.FUNC_LEN_STR, Syscall.FUNC_LEN_STRS, Syscall.FUNC_LEN_STRP, Syscall.FUNC_LEN_STRPS -> { + val strPtr = evalstack.pop().integerValue() + val text = heap.get(strPtr).str!! + evalstack.push(Value(DataType.UBYTE, text.length)) + } Syscall.FUNC_SIN -> evalstack.push(Value(DataType.FLOAT, sin(evalstack.pop().numericValue().toDouble()))) Syscall.FUNC_COS -> evalstack.push(Value(DataType.FLOAT, cos(evalstack.pop().numericValue().toDouble()))) Syscall.FUNC_ROUND -> evalstack.push(Value(DataType.WORD, evalstack.pop().numericValue().toDouble().roundToInt())) @@ -1565,7 +1572,7 @@ class StackVm(private var traceOutputFile: String?) { } Syscall.FUNC_STR2BYTE -> { val strvar = evalstack.pop() - val str = heap.get(strvar.heapId) + val str = heap.get(strvar.heapId) // TODO CHECK val y = str.str!!.trim().trimEnd('\u0000') evalstack.push(Value(DataType.BYTE, y.toShort())) } diff --git a/prog8lib/prog8lib.p8 b/prog8lib/prog8lib.p8 index e6b597f76..a30f43717 100644 --- a/prog8lib/prog8lib.p8 +++ b/prog8lib/prog8lib.p8 @@ -897,11 +897,6 @@ func_sum .proc .warn "sum not implemented--what does it sum over???" .pend -func_len .proc - rts - .warn "len not implemented--of what does it take len?" - .pend - func_any .proc rts .warn "any not implemented--of what does it do any?" @@ -912,6 +907,33 @@ func_all .proc .warn "all not implemented--of what does it do all?" .pend +func_len_str .proc + ; -- push length of 0-terminated string on stack + lda ESTACK_LO+1,x + sta SCRATCH_ZPWORD1 + lda ESTACK_HI+1,x + sta SCRATCH_ZPWORD1+1 + ldy #0 +- lda (SCRATCH_ZPWORD1),y + beq + + iny + bne - ++ tya + sta ESTACK_LO+1,x + rts + .pend + +func_len_strp .proc + ; -- push length of pascal-string on stack + lda ESTACK_LO+1,x + sta SCRATCH_ZPWORD1 + lda ESTACK_HI+1,x + sta SCRATCH_ZPWORD1+1 + ldy #0 + lda (SCRATCH_ZPWORD1),y ; first byte is length + sta ESTACK_LO+1,x + rts + .pend func_rnd .proc ; -- put a random ubyte on the estack