diff --git a/compiler/examples/test.p8 b/compiler/examples/test.p8 index e9f00a60a..ae4df5aa1 100644 --- a/compiler/examples/test.p8 +++ b/compiler/examples/test.p8 @@ -14,76 +14,12 @@ sub start() { word[5] warrayvar - set_carry() - clear_carry() - set_irqd() - clear_irqd() + fvar=test(34455) + return - rol(bvar) - rol(wvar) - rol(fvar) - rol(svar) - rol(spvar) - rol(ssvar) - rol(spsvar) - rol(matrixvar) - rol(barrayvar) - rol(warrayvar) - - rol2(bvar) - rol2(wvar) - rol2(fvar) - rol2(svar) - rol2(spvar) - rol2(ssvar) - rol2(spsvar) - rol2(matrixvar) - rol2(barrayvar) - rol2(warrayvar) - - ror(bvar) - ror(wvar) - ror(fvar) - ror(svar) - ror(spvar) - ror(ssvar) - ror(spsvar) - ror(matrixvar) - ror(barrayvar) - ror(warrayvar) - - ror2(bvar) - ror2(wvar) - ror2(fvar) - ror2(svar) - ror2(spvar) - ror2(ssvar) - ror2(spsvar) - ror2(matrixvar) - ror2(barrayvar) - ror2(warrayvar) - - lsl(bvar) - lsl(wvar) - lsl(fvar) - lsl(svar) - lsl(spvar) - lsl(ssvar) - lsl(spsvar) - lsl(matrixvar) - lsl(barrayvar) - lsl(warrayvar) - - lsr(bvar) - lsr(wvar) - lsr(fvar) - lsr(svar) - lsr(spvar) - lsr(ssvar) - lsr(spsvar) - lsr(matrixvar) - lsr(barrayvar) - lsr(warrayvar) +sub test(arg: byte) -> float { + return 44.54 +} } } diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index e78944d2a..f5e78b211 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -83,7 +83,10 @@ class AstChecker(private val namespace: INameScope, if(expectedReturnValues.size != returnStmt.values.size) checkResult.add(SyntaxError("number of return values doesn't match subroutine return spec", returnStmt.position)) - // @todo: check return value types versus sub return spec + for (rv in expectedReturnValues.withIndex().zip(returnStmt.values)) { + if(rv.first.value!=rv.second.resultingDatatype(namespace, heap)) + checkResult.add(ExpressionError("type of return value ${rv.first.index+1} doesn't match subroutine return type ${rv.first.value}", rv.second.position)) + } return super.process(returnStmt) } @@ -193,14 +196,13 @@ class AstChecker(private val namespace: INameScope, // (or if it has an asm block, that must contain a 'rts' or 'jmp') if(subroutine.statements.count { it is Return || it is Jump } == 0) { if(subroutine.address==null) { - val amount = subroutine.statements - .asSequence() - .filter { it is InlineAssembly } - .map {(it as InlineAssembly).assembly} - .count { "rts" in it || "\trts" in it || "jmp" in it || "\tjmp" in it } - if(amount==0 && subroutine.returnvalues.isNotEmpty()) - err("subroutine has result value(s) and thus must have at least one 'return' or 'goto' in it (or 'rts' / 'jmp' in case of %asm)") - // @todo validate return values versus subroutine signature + val amount = subroutine.statements + .asSequence() + .filter { it is InlineAssembly } + .map { (it as InlineAssembly).assembly } + .count { "rts" in it || "\trts" in it || "jmp" in it || "\tjmp" in it } + if (amount == 0 && subroutine.returnvalues.isNotEmpty()) + err("subroutine has result value(s) and thus must have at least one 'return' or 'goto' in it (or 'rts' / 'jmp' in case of %asm)") } } @@ -519,15 +521,25 @@ class AstChecker(private val namespace: INameScope, checkResult.add(SyntaxError("cannot use arguments when calling a label", position)) if(target is BuiltinFunctionStatementPlaceholder) { - // it's a cal to a builtin function. - // todo make the signature checking similar to when calling a user-defined function - if(target.name=="set_carry" || target.name=="set_irqd" || target.name=="clear_carry" || target.name=="clear_irqd") { - // these functions have zero arguments - if(args.isNotEmpty()) - checkResult.add(SyntaxError("${target.name} has zero arguments", position)) + // it's a call to a builtin function. + val func = BuiltinFunctions[target.name]!! + if(args.size!=func.parameters.size) + checkResult.add(SyntaxError("invalid number of parameters", position)) + else { + for (arg in args.withIndex().zip(func.parameters)) { + if(arg.first.value.resultingDatatype(namespace, heap) !in arg.second.possibleDatatypes) + checkResult.add(SyntaxError("argument ${arg.first.index+1} has invalid type, expected ${arg.second.possibleDatatypes}", position)) + } + } + } else if(target is Subroutine) { + if(args.size!=target.parameters.size) + checkResult.add(SyntaxError("invalid number of parameters", position)) + else { + for (arg in args.withIndex().zip(target.parameters)) { + if(arg.first.value.resultingDatatype(namespace, heap) != arg.second.type) + checkResult.add(SyntaxError("argument ${arg.first.index+1} has invalid type, expected ${arg.second.type}", position)) + } } - } else { - // @todo check call (params against signature) to user function } } diff --git a/compiler/src/prog8/functions/BuiltinFunctions.kt b/compiler/src/prog8/functions/BuiltinFunctions.kt index 69e9caa4a..d2d0432aa 100644 --- a/compiler/src/prog8/functions/BuiltinFunctions.kt +++ b/compiler/src/prog8/functions/BuiltinFunctions.kt @@ -6,7 +6,7 @@ import kotlin.math.log2 -class BuiltinFunctionParam(val name: String, possibleDatatypes: List) +class BuiltinFunctionParam(val name: String, val possibleDatatypes: List) class FunctionSignature(val pure: Boolean, // does it have side effects? val parameters: List, @@ -67,15 +67,6 @@ val BuiltinFunctions = mapOf( fun builtinFunctionReturnType(function: String, args: List, namespace: INameScope, heap: HeapValues): DataType? { - fun integerDatatypeFromArg(arg: IExpression): DataType { - val dt = arg.resultingDatatype(namespace, heap) - return when(dt) { - DataType.BYTE -> DataType.BYTE - DataType.WORD -> DataType.WORD - DataType.FLOAT -> DataType.WORD - else -> throw FatalAstException("fuction $function can only return a numeric value") - } - } fun datatypeFromListArg(arglist: IExpression): DataType { if(arglist is LiteralValue) { @@ -119,7 +110,15 @@ fun builtinFunctionReturnType(function: String, args: List, namespa DataType.MATRIX -> DataType.BYTE } } - "round", "floor", "ceil" -> integerDatatypeFromArg(args.single()) + "round", "floor", "ceil" -> { + val dt=args.single().resultingDatatype(namespace, heap) + when(dt) { + DataType.BYTE -> DataType.BYTE + DataType.WORD -> DataType.WORD + DataType.FLOAT -> DataType.WORD + else -> null + } + } "sum" -> { val dt=datatypeFromListArg(args.single()) when(dt) { @@ -205,8 +204,15 @@ private fun collectionArgOutputNumber(args: List, position: Positio throw NotConstArgumentException() function(constants.map { it!!.toDouble() }).toDouble() } else { - val array = heap.get(iterable.heapId!!).array ?: throw SyntaxError("function requires array/matrix argument", position) - function(array.map { it.toDouble() }) + when(iterable.type) { + DataType.BYTE, DataType.WORD, DataType.FLOAT -> throw SyntaxError("function expects an iterable type", position) + else -> { + if(iterable.heapId==null) + throw FatalAstException("iterable value should be on the heap") + val array = heap.get(iterable.heapId).array ?: throw SyntaxError("function expects an iterable type", position) + function(array.map { it.toDouble() }) + } + } } return numericLiteral(result, args[0].position) }