diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index 949cf3ad6..d8db59e6e 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -442,11 +442,11 @@ class AsmGen(internal val program: Program, internal fun translateBuiltinFunctionCallExpression(bfc: BuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) = builtinFunctionsAsmGen.translateFunctioncallExpression(bfc, resultToStack, resultRegister) - private fun translateBuiltinFunctionCallExpression(name: String, singleArg:AsmAssignSource, scope: Subroutine): DataType = - builtinFunctionsAsmGen.translateUnaryFunctioncall(name, singleArg, false, scope) + private fun translateBuiltinFunctionCallExpression(bfc: IFunctionCall, firstArg: AsmAssignSource, scope: Subroutine): DataType = + builtinFunctionsAsmGen.translateFunctionCallWithFirstArg(bfc, firstArg, false, scope) - private fun translateBuiltinFunctionCallStatement(name: String, singleArg: AsmAssignSource, scope: Subroutine) = - builtinFunctionsAsmGen.translateUnaryFunctioncall(name, singleArg, true, scope) + private fun translateBuiltinFunctionCallStatement(bfc: IFunctionCall, firstArg: AsmAssignSource, scope: Subroutine) = + builtinFunctionsAsmGen.translateFunctionCallWithFirstArg(bfc, firstArg, true, scope) internal fun translateFunctionCall(functionCallExpr: FunctionCallExpression, isExpression: Boolean) = functioncallAsmGen.translateFunctionCall(functionCallExpr, isExpression) @@ -2829,20 +2829,20 @@ $repeatLabel lda $counterVar AsmAssignSource.fromAstSource(source, program, this) } - // the segments (except the last one): unary function calls taking a single param and producing a value. - // directly assign their argument from the previous call's returnvalue. + // the segments (except the last one): function calls taking one or more parameters and producing a value. + // directly assign their first argument from the previous call's returnvalue (and take the rest, if any, from the call itself) segments.dropLast(1).forEach { it as IFunctionCall - valueDt = translateUnaryFunctionCallWithArgSource(it.target, valueSource, false, subroutine) + valueDt = translateFunctionCallWithFirstArg(it, valueSource, false, subroutine) val resultReg = returnRegisterOfFunction(it.target) valueSource = AsmAssignSource(SourceStorageKind.REGISTER, program, this, valueDt, register = resultReg) } - // the last segment: unary function call taking a single param and optionally producing a result value. + // the last segment: function call taking one or more parameters and optionally producing a result value. val lastCall = segments.last() as IFunctionCall if(isStatement) { - translateUnaryFunctionCallWithArgSource(lastCall.target, valueSource, true, subroutine) + translateFunctionCallWithFirstArg(lastCall, valueSource, true, subroutine) } else { - valueDt = translateUnaryFunctionCallWithArgSource(lastCall.target, valueSource, false, subroutine) + valueDt = translateFunctionCallWithFirstArg(lastCall, valueSource, false, subroutine) if(pushResultOnEstack) { when (valueDt) { in ByteDatatypes -> { @@ -2860,14 +2860,23 @@ $repeatLabel lda $counterVar } } - private fun translateUnaryFunctionCallWithArgSource(target: IdentifierReference, singleArg: AsmAssignSource, isStatement: Boolean, scope: Subroutine): DataType { - when(val targetStmt = target.targetStatement(program)!!) { + private fun translateFunctionCallWithFirstArg( + fcall: IFunctionCall, + firstArg: AsmAssignSource, + isStatement: Boolean, + scope: Subroutine + ): DataType { + + if(fcall.args.isNotEmpty()) + TODO("deal with additional args (non-unary function): ${fcall.target.nameInSource} (... , ${fcall.args.joinToString(", ")})") + + when(val targetStmt = fcall.target.targetStatement(program)!!) { is BuiltinFunctionPlaceholder -> { return if(isStatement) { - translateBuiltinFunctionCallStatement(targetStmt.name, singleArg, scope) + translateBuiltinFunctionCallStatement(fcall, firstArg, scope) DataType.UNDEFINED } else { - translateBuiltinFunctionCallExpression(targetStmt.name, singleArg, scope) + translateBuiltinFunctionCallExpression(fcall, firstArg, scope) } } is Subroutine -> { @@ -2876,9 +2885,9 @@ $repeatLabel lda $counterVar // argument via registers val argRegister = targetStmt.asmParameterRegisters.single().registerOrPair!! val assignArgument = AsmAssignment( - singleArg, + firstArg, AsmAssignTarget.fromRegisters(argRegister, argDt in SignedDatatypes, scope, program, this), - false, program.memsizer, target.position + false, program.memsizer, fcall.position ) translateNormalAssignment(assignArgument) } else { @@ -2892,24 +2901,24 @@ $repeatLabel lda $counterVar else -> throw AssemblyError("invalid dt") } AsmAssignment( - singleArg, + firstArg, AsmAssignTarget(TargetStorageKind.REGISTER, program, this, argDt, scope, register = paramReg), - false, program.memsizer, target.position + false, program.memsizer, fcall.position ) } else { // arg goes via parameter variable val argVarName = asmVariableName(targetStmt.scopedName + targetStmt.parameters.single().name) AsmAssignment( - singleArg, + firstArg, AsmAssignTarget(TargetStorageKind.VARIABLE, program, this, argDt, scope, argVarName), - false, program.memsizer, target.position + false, program.memsizer, fcall.position ) } translateNormalAssignment(assignArgument) } if(targetStmt.shouldSaveX()) saveRegisterLocal(CpuRegister.X, scope) - out(" jsr ${asmSymbolName(target)}") + out(" jsr ${asmSymbolName(fcall.target)}") if(targetStmt.shouldSaveX()) restoreRegisterLocal(CpuRegister.X) return if(isStatement) DataType.UNDEFINED else targetStmt.returntypes.single() diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt index b184ed697..e9508ee27 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt @@ -30,7 +30,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, translateFunctioncall(fcall, func, discardResult = true, resultToStack = false, resultRegister = null) } - internal fun translateUnaryFunctioncall(name: String, singleArg: AsmAssignSource, isStatement: Boolean, scope: Subroutine): DataType { + internal fun translateFunctionCallWithFirstArg(bfc: IFunctionCall, singleArg: AsmAssignSource, isStatement: Boolean, scope: Subroutine): DataType { + val name = bfc.target.nameInSource.single() val func = BuiltinFunctions.getValue(name) val argExpression = when(singleArg.kind) { diff --git a/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt b/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt index 40712b597..e1c6f5d97 100644 --- a/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt +++ b/compiler/src/prog8/compiler/astprocessing/VerifyFunctionArgTypes.kt @@ -158,8 +158,8 @@ internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorRe when (target) { is BuiltinFunctionPlaceholder -> { val func = BuiltinFunctions.getValue(target.name) - if(func.parameters.size!=1) - errors.err("can only use unary function", funccall.position) + if(func.parameters.isEmpty()) + errors.err("function must have at least one parameter", funccall.position) else if(func.returnType==null && funccall !== segments.last()) errors.err("function must return a single value", funccall.position) @@ -168,14 +168,12 @@ internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorRe errors.err("pipe value datatype $valueDt incompatible with function argument ${paramDts.toList()}", funccall.position) if(errors.noErrors()) { - // type can depend on the argument(s) of the function. For now, we only deal with unary functions, - // so we know there must be a single argument. Take its type from the previous expression in the pipe chain. valueDt = builtinFunctionReturnType(func.name).getOrElse { DataType.UNDEFINED } } } is Subroutine -> { - if(target.parameters.size!=1) - errors.err("can only use unary function", funccall.position) + if(target.parameters.isEmpty()) + errors.err("function must have at least one parameter", funccall.position) else if(target.returntypes.size!=1 && funccall !== segments.last()) errors.err("function must return a single value", funccall.position) diff --git a/examples/test.p8 b/examples/test.p8 index f6c30d0b7..a17b0cd6b 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -7,9 +7,17 @@ main { + sub add(ubyte first, ubyte second) -> ubyte { + return first+second + } + + sub mul(ubyte first, ubyte second) -> ubyte { + return first*second + } + sub start() { ubyte source=99 - ubyte value= source |> math.sin8u() |> math.cos8u() + ubyte value = add(3,4) |> add(10) |> mul(2) |> math.sin8u() ; TODO should not work yet on vm codegen, but it compiles.... :/ txt.print_ub(value) ; expected output: aaabbb aaa bbb