From 12dc7e97338ddea92fc96de285800a53b6c38cc8 Mon Sep 17 00:00:00 2001 From: Giorgio Garofalo Date: Tue, 30 Jul 2024 13:19:40 +0200 Subject: [PATCH] Replace FunctionArgumentsLinker with new ArgumentBindings --- .../eu/iamgio/quarkdown/function/Function.kt | 9 +++-- .../function/call/FunctionArgumentsLinker.kt | 40 ------------------- .../quarkdown/function/call/FunctionCall.kt | 9 ++--- .../function/call/binding/ArgumentsBinder.kt | 2 + .../function/reflect/KFunctionAdapter.kt | 8 ++-- .../quarkdown/StandaloneFunctionTest.kt | 32 ++++++++++----- .../kotlin/eu/iamgio/quarkdown/stdlib/Flow.kt | 4 +- 7 files changed, 40 insertions(+), 64 deletions(-) delete mode 100644 core/src/main/kotlin/eu/iamgio/quarkdown/function/call/FunctionArgumentsLinker.kt diff --git a/core/src/main/kotlin/eu/iamgio/quarkdown/function/Function.kt b/core/src/main/kotlin/eu/iamgio/quarkdown/function/Function.kt index 3bf9cd21..f0085b0f 100644 --- a/core/src/main/kotlin/eu/iamgio/quarkdown/function/Function.kt +++ b/core/src/main/kotlin/eu/iamgio/quarkdown/function/Function.kt @@ -1,6 +1,7 @@ package eu.iamgio.quarkdown.function -import eu.iamgio.quarkdown.function.call.FunctionArgumentsLinker +import eu.iamgio.quarkdown.function.call.FunctionCall +import eu.iamgio.quarkdown.function.call.binding.ArgumentBindings import eu.iamgio.quarkdown.function.value.OutputValue /** @@ -21,9 +22,9 @@ interface Function> { /** * Function that maps the input arguments into an output value. * Arguments and [parameters] compliance in terms of matching types and count is not checked here. - * The [FunctionArgumentsLinker] allows looking up argument values by their parameter name. + * The [ArgumentBindings] allow looking up argument values by their parameter. */ - val invoke: FunctionArgumentsLinker.() -> T + val invoke: (ArgumentBindings) -> T } /** @@ -33,7 +34,7 @@ interface Function> { data class SimpleFunction>( override val name: String, override val parameters: List>, - override val invoke: FunctionArgumentsLinker.() -> T, + override val invoke: (ArgumentBindings) -> T, ) : Function fun Function<*>.asString() = diff --git a/core/src/main/kotlin/eu/iamgio/quarkdown/function/call/FunctionArgumentsLinker.kt b/core/src/main/kotlin/eu/iamgio/quarkdown/function/call/FunctionArgumentsLinker.kt deleted file mode 100644 index e7c48113..00000000 --- a/core/src/main/kotlin/eu/iamgio/quarkdown/function/call/FunctionArgumentsLinker.kt +++ /dev/null @@ -1,40 +0,0 @@ -package eu.iamgio.quarkdown.function.call - -import eu.iamgio.quarkdown.function.FunctionParameter -import eu.iamgio.quarkdown.function.call.binding.AllArgumentsBinder - -/** - * Parameter-argument pairs for a function call. - */ -private typealias Links = Map, FunctionCallArgument> - -/** - * Helper that associates [FunctionCallArgument]s to their corresponding [FunctionParameter]. - * @param call function call to link arguments for - */ -class FunctionArgumentsLinker(private val call: FunctionCall<*>) { - lateinit var links: Links - - /** - * Stores the associations between [FunctionCallArgument]s and [FunctionParameter]s. - * @throws eu.iamgio.quarkdown.function.error.InvalidFunctionCallException or subclass - * if there is a mismatch between arguments and parameters - */ - fun link() { - this.links = AllArgumentsBinder(call).createBindings(call.function.parameters) - } - - /** - * @param name name of the parameter to get the corresponding argument value for - * @param T type of the value - * @return the value of the argument by the given name - * @throws NoSuchElementException if [name] does not match any parameter name - * @deprecated used in tests only - */ - inline fun arg(name: String): T = - this.links.entries - .first { it.key.name == name } - .value // Map.Entry method: returns FunctionCallArgument - .value // FunctionCallArgument method: returns InputValue - .unwrappedValue as T // InputValue method: returns T -} diff --git a/core/src/main/kotlin/eu/iamgio/quarkdown/function/call/FunctionCall.kt b/core/src/main/kotlin/eu/iamgio/quarkdown/function/call/FunctionCall.kt index 82db13dc..67d8c17e 100644 --- a/core/src/main/kotlin/eu/iamgio/quarkdown/function/call/FunctionCall.kt +++ b/core/src/main/kotlin/eu/iamgio/quarkdown/function/call/FunctionCall.kt @@ -2,6 +2,7 @@ package eu.iamgio.quarkdown.function.call import eu.iamgio.quarkdown.context.Context import eu.iamgio.quarkdown.function.Function +import eu.iamgio.quarkdown.function.call.binding.AllArgumentsBinder import eu.iamgio.quarkdown.function.expression.Expression import eu.iamgio.quarkdown.function.expression.visitor.ExpressionVisitor import eu.iamgio.quarkdown.function.value.OutputValue @@ -25,11 +26,9 @@ data class FunctionCall>( * @return the function output */ fun execute(): T { - // Allows linking arguments to their parameter. - val linker = FunctionArgumentsLinker(this) - linker.link() - - return function.invoke(linker) + // Allows binding each argument to its parameter. + val bindings = AllArgumentsBinder(this).createBindings(function.parameters) + return function.invoke(bindings) } override fun accept(visitor: ExpressionVisitor): T = visitor.visit(this) diff --git a/core/src/main/kotlin/eu/iamgio/quarkdown/function/call/binding/ArgumentsBinder.kt b/core/src/main/kotlin/eu/iamgio/quarkdown/function/call/binding/ArgumentsBinder.kt index 72b8b0dc..f1a1ea8a 100644 --- a/core/src/main/kotlin/eu/iamgio/quarkdown/function/call/binding/ArgumentsBinder.kt +++ b/core/src/main/kotlin/eu/iamgio/quarkdown/function/call/binding/ArgumentsBinder.kt @@ -11,6 +11,8 @@ typealias ArgumentBindings = Map, FunctionCallArgument> /** * Builder of parameter-argument pairs of a function call. + * Allows binding each argument to its corresponding parameter, + * and may throw an exception if some cannot be paired. * @see InjectedArgumentsBinder * @see InjectedArgumentsBinder * @see AllArgumentsBinder diff --git a/core/src/main/kotlin/eu/iamgio/quarkdown/function/reflect/KFunctionAdapter.kt b/core/src/main/kotlin/eu/iamgio/quarkdown/function/reflect/KFunctionAdapter.kt index 6a894541..ace06d76 100644 --- a/core/src/main/kotlin/eu/iamgio/quarkdown/function/reflect/KFunctionAdapter.kt +++ b/core/src/main/kotlin/eu/iamgio/quarkdown/function/reflect/KFunctionAdapter.kt @@ -2,7 +2,7 @@ package eu.iamgio.quarkdown.function.reflect import eu.iamgio.quarkdown.function.Function import eu.iamgio.quarkdown.function.FunctionParameter -import eu.iamgio.quarkdown.function.call.FunctionArgumentsLinker +import eu.iamgio.quarkdown.function.call.binding.ArgumentBindings import eu.iamgio.quarkdown.function.error.FunctionRuntimeException import eu.iamgio.quarkdown.function.value.InputValue import eu.iamgio.quarkdown.function.value.OutputValue @@ -39,10 +39,10 @@ class KFunctionAdapter>(private val function: KFunction) : ) } - override val invoke: FunctionArgumentsLinker.() -> T - get() = { + override val invoke: (ArgumentBindings) -> T + get() = { bindings -> val args = - this.links.asSequence().associate { (parameter, argument) -> + bindings.asSequence().associate { (parameter, argument) -> // Corresponding KParameter. val param = function.parameters[parameter.index] diff --git a/core/src/test/kotlin/eu/iamgio/quarkdown/StandaloneFunctionTest.kt b/core/src/test/kotlin/eu/iamgio/quarkdown/StandaloneFunctionTest.kt index 6a752285..53020068 100644 --- a/core/src/test/kotlin/eu/iamgio/quarkdown/StandaloneFunctionTest.kt +++ b/core/src/test/kotlin/eu/iamgio/quarkdown/StandaloneFunctionTest.kt @@ -9,6 +9,7 @@ import eu.iamgio.quarkdown.function.FunctionParameter import eu.iamgio.quarkdown.function.SimpleFunction import eu.iamgio.quarkdown.function.call.FunctionCall import eu.iamgio.quarkdown.function.call.FunctionCallArgument +import eu.iamgio.quarkdown.function.call.binding.ArgumentBindings import eu.iamgio.quarkdown.function.error.InvalidArgumentCountException import eu.iamgio.quarkdown.function.error.MismatchingArgumentTypeException import eu.iamgio.quarkdown.function.error.NoSuchElementFunctionException @@ -34,6 +35,19 @@ import kotlin.test.assertNull * For tests of function calls from Quarkdown sources see [FunctionNodeExpansionTest]. */ class StandaloneFunctionTest { + /** + * @param name name of the parameter to get the corresponding argument value for + * @param T type of the value + * @return the value of the argument by the given name + * @throws NoSuchElementException if [name] does not match any parameter name + */ + private inline fun ArgumentBindings.arg(name: String): T = + this.entries + .first { it.key.name == name } + .value // Map.Entry method: returns FunctionCallArgument + .value // FunctionCallArgument method: returns InputValue + .unwrappedValue as T // InputValue method: returns T + @Test fun `no arguments`() { val function = @@ -59,9 +73,9 @@ class StandaloneFunctionTest { FunctionParameter("to", StringValue::class, index = 0), FunctionParameter("from", StringValue::class, index = 1), ), - ) { - val to = arg("to") - val from = arg("from") + ) { bindings -> + val to = bindings.arg("to") + val from = bindings.arg("from") ValueFactory.string("Hello $to from $from") } @@ -96,9 +110,9 @@ class StandaloneFunctionTest { FunctionParameter("to", StringValue::class, index = 0), FunctionParameter("from", StringValue::class, index = 1), ), - ) { - val to = arg("to") - val from = arg("from") + ) { bindings -> + val to = bindings.arg("to") + val from = bindings.arg("from") ValueFactory.string("Hello $to from $from") } @@ -139,9 +153,9 @@ class StandaloneFunctionTest { FunctionParameter("to", StringValue::class, index = 0), FunctionParameter("from", StringValue::class, index = 1), ), - ) { - val to = arg("to") - val from = arg("from") + ) { bindings -> + val to = bindings.arg("to") + val from = bindings.arg("from") ValueFactory.string("Hello $to from $from") } diff --git a/stdlib/src/main/kotlin/eu/iamgio/quarkdown/stdlib/Flow.kt b/stdlib/src/main/kotlin/eu/iamgio/quarkdown/stdlib/Flow.kt index 1678abd7..01ffb5dd 100644 --- a/stdlib/src/main/kotlin/eu/iamgio/quarkdown/stdlib/Flow.kt +++ b/stdlib/src/main/kotlin/eu/iamgio/quarkdown/stdlib/Flow.kt @@ -114,8 +114,8 @@ fun function( // The custom function itself. val function = - SimpleFunction(name, parameters) { - val args = this.links.values.map { it.value }.toTypedArray() + SimpleFunction(name, parameters) { bindings -> + val args = bindings.values.map { it.value }.toTypedArray() // The final result is evaluated and returned as a dynamic, hence it can be used as any type. body.invokeDynamic(*args)