first steps to support multiple args in pipe expressions

This commit is contained in:
Irmen de Jong 2022-05-07 18:44:23 +02:00
parent 942c5cc04b
commit 7c121bfc01
4 changed files with 45 additions and 29 deletions

View File

@ -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()

View File

@ -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) {

View File

@ -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)

View File

@ -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