funcion call parameters are now checked against function signature

This commit is contained in:
Irmen de Jong 2018-09-30 20:58:47 +02:00
parent d1589bef26
commit 734948c813
3 changed files with 53 additions and 99 deletions

View File

@ -14,76 +14,12 @@ sub start() {
word[5] warrayvar word[5] warrayvar
set_carry() fvar=test(34455)
clear_carry() return
set_irqd()
clear_irqd()
rol(bvar) sub test(arg: byte) -> float {
rol(wvar) return 44.54
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)
} }
} }

View File

@ -83,7 +83,10 @@ class AstChecker(private val namespace: INameScope,
if(expectedReturnValues.size != returnStmt.values.size) if(expectedReturnValues.size != returnStmt.values.size)
checkResult.add(SyntaxError("number of return values doesn't match subroutine return spec", returnStmt.position)) 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) 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') // (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.statements.count { it is Return || it is Jump } == 0) {
if(subroutine.address==null) { if(subroutine.address==null) {
val amount = subroutine.statements val amount = subroutine.statements
.asSequence() .asSequence()
.filter { it is InlineAssembly } .filter { it is InlineAssembly }
.map {(it as InlineAssembly).assembly} .map { (it as InlineAssembly).assembly }
.count { "rts" in it || "\trts" in it || "jmp" in it || "\tjmp" in it } .count { "rts" in it || "\trts" in it || "jmp" in it || "\tjmp" in it }
if(amount==0 && subroutine.returnvalues.isNotEmpty()) 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)") 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
} }
} }
@ -519,15 +521,25 @@ class AstChecker(private val namespace: INameScope,
checkResult.add(SyntaxError("cannot use arguments when calling a label", position)) checkResult.add(SyntaxError("cannot use arguments when calling a label", position))
if(target is BuiltinFunctionStatementPlaceholder) { if(target is BuiltinFunctionStatementPlaceholder) {
// it's a cal to a builtin function. // it's a call to a builtin function.
// todo make the signature checking similar to when calling a user-defined function val func = BuiltinFunctions[target.name]!!
if(target.name=="set_carry" || target.name=="set_irqd" || target.name=="clear_carry" || target.name=="clear_irqd") { if(args.size!=func.parameters.size)
// these functions have zero arguments checkResult.add(SyntaxError("invalid number of parameters", position))
if(args.isNotEmpty()) else {
checkResult.add(SyntaxError("${target.name} has zero arguments", position)) 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
} }
} }

View File

@ -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? class FunctionSignature(val pure: Boolean, // does it have side effects?
val parameters: List<BuiltinFunctionParam>, val parameters: List<BuiltinFunctionParam>,
@ -67,15 +67,6 @@ val BuiltinFunctions = mapOf(
fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespace: INameScope, heap: HeapValues): DataType? { 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 { fun datatypeFromListArg(arglist: IExpression): DataType {
if(arglist is LiteralValue) { if(arglist is LiteralValue) {
@ -119,7 +110,15 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, namespa
DataType.MATRIX -> DataType.BYTE 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" -> { "sum" -> {
val dt=datatypeFromListArg(args.single()) val dt=datatypeFromListArg(args.single())
when(dt) { when(dt) {
@ -205,8 +204,15 @@ private fun collectionArgOutputNumber(args: List<IExpression>, position: Positio
throw NotConstArgumentException() throw NotConstArgumentException()
function(constants.map { it!!.toDouble() }).toDouble() function(constants.map { it!!.toDouble() }).toDouble()
} else { } else {
val array = heap.get(iterable.heapId!!).array ?: throw SyntaxError("function requires array/matrix argument", position) when(iterable.type) {
function(array.map { it.toDouble() }) 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) return numericLiteral(result, args[0].position)
} }