mirror of
https://github.com/irmen/prog8.git
synced 2024-11-26 11:49:22 +00:00
funcion call parameters are now checked against function signature
This commit is contained in:
parent
d1589bef26
commit
734948c813
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ import kotlin.math.log2
|
||||
|
||||
|
||||
|
||||
class BuiltinFunctionParam(val name: String, possibleDatatypes: List<DataType>)
|
||||
class BuiltinFunctionParam(val name: String, val possibleDatatypes: List<DataType>)
|
||||
|
||||
class FunctionSignature(val pure: Boolean, // does it have side effects?
|
||||
val parameters: List<BuiltinFunctionParam>,
|
||||
@ -67,15 +67,6 @@ val BuiltinFunctions = mapOf(
|
||||
|
||||
|
||||
fun builtinFunctionReturnType(function: String, args: List<IExpression>, 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<IExpression>, 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<IExpression>, 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)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user