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
|
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)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user