-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
164 additions
and
104 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
core/src/main/kotlin/eu/iamgio/quarkdown/function/call/binding/AllArgumentsBinder.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package eu.iamgio.quarkdown.function.call.binding | ||
|
||
import eu.iamgio.quarkdown.function.FunctionParameter | ||
import eu.iamgio.quarkdown.function.call.FunctionCall | ||
import eu.iamgio.quarkdown.function.error.InvalidArgumentCountException | ||
|
||
/** | ||
* Builder of bindings for all arguments of a function call. | ||
* @param call function call to bind arguments for | ||
* @see RegularArgumentsBinder | ||
* @see InjectedArgumentsBinder | ||
*/ | ||
class AllArgumentsBinder(private val call: FunctionCall<*>) : ArgumentsBinder { | ||
/** | ||
* Joins the results of the subsets of regular ([RegularArgumentsBinder]) | ||
* and injected ([InjectedArgumentsBinder]) arguments. | ||
*/ | ||
override fun createBindings(parameters: List<FunctionParameter<*>>): ArgumentBindings { | ||
val (injected, regular) = call.function.parameters.partition { it.isInjected } | ||
|
||
// Argument-parameter links are generated for both types of parameters and joined together. | ||
val bindings = | ||
RegularArgumentsBinder(call).createBindings(regular) + | ||
InjectedArgumentsBinder(call).createBindings(injected) | ||
|
||
// If mandatory params count > args count. | ||
if (call.function.parameters.any { !it.isOptional && it !in bindings }) { | ||
throw InvalidArgumentCountException(call) | ||
} | ||
|
||
return bindings | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
core/src/main/kotlin/eu/iamgio/quarkdown/function/call/binding/ArgumentsBinder.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package eu.iamgio.quarkdown.function.call.binding | ||
|
||
import eu.iamgio.quarkdown.function.FunctionParameter | ||
import eu.iamgio.quarkdown.function.call.FunctionCallArgument | ||
import eu.iamgio.quarkdown.function.error.InvalidFunctionCallException | ||
|
||
/** | ||
* Parameter-argument pairs of a function call. | ||
*/ | ||
typealias ArgumentBindings = Map<FunctionParameter<*>, FunctionCallArgument> | ||
|
||
/** | ||
* Builder of parameter-argument pairs of a function call. | ||
* @see InjectedArgumentsBinder | ||
* @see InjectedArgumentsBinder | ||
* @see AllArgumentsBinder | ||
*/ | ||
sealed interface ArgumentsBinder { | ||
/** | ||
* @param parameters parameters of the called function (or a subset of them) | ||
* @return the parameter-argument pairs | ||
* @throws InvalidFunctionCallException or subclass if there is arguments and parameters cannot be paired | ||
*/ | ||
fun createBindings(parameters: List<FunctionParameter<*>>): ArgumentBindings | ||
} |
19 changes: 19 additions & 0 deletions
19
core/src/main/kotlin/eu/iamgio/quarkdown/function/call/binding/InjectedArgumentsBinder.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package eu.iamgio.quarkdown.function.call.binding | ||
|
||
import eu.iamgio.quarkdown.function.FunctionParameter | ||
import eu.iamgio.quarkdown.function.call.FunctionCall | ||
import eu.iamgio.quarkdown.function.call.FunctionCallArgument | ||
import eu.iamgio.quarkdown.function.reflect.InjectedValue | ||
|
||
/** | ||
* Builder of bindings for the injected argument subset of a function call. | ||
* @param call function call to bind arguments for | ||
* @see FunctionParameter.isInjected | ||
*/ | ||
class InjectedArgumentsBinder(private val call: FunctionCall<*>) : ArgumentsBinder { | ||
override fun createBindings(parameters: List<FunctionParameter<*>>) = | ||
parameters.associateWith { | ||
val value = InjectedValue.fromType(it.type, call) | ||
FunctionCallArgument(value) | ||
} | ||
} |
85 changes: 85 additions & 0 deletions
85
core/src/main/kotlin/eu/iamgio/quarkdown/function/call/binding/RegularArgumentsBinder.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package eu.iamgio.quarkdown.function.call.binding | ||
|
||
import eu.iamgio.quarkdown.function.FunctionParameter | ||
import eu.iamgio.quarkdown.function.call.FunctionCall | ||
import eu.iamgio.quarkdown.function.error.InvalidArgumentCountException | ||
import eu.iamgio.quarkdown.function.error.MismatchingArgumentTypeException | ||
import eu.iamgio.quarkdown.function.error.UnnamedArgumentAfterNamedException | ||
import eu.iamgio.quarkdown.function.error.UnresolvedParameterException | ||
import eu.iamgio.quarkdown.function.reflect.DynamicValueConverter | ||
import eu.iamgio.quarkdown.function.value.DynamicValue | ||
import eu.iamgio.quarkdown.function.value.StringValue | ||
import eu.iamgio.quarkdown.function.value.ValueFactory | ||
import kotlin.reflect.full.isSubclassOf | ||
|
||
/** | ||
* Builder of bindings for the regular (not injected) argument subset of a function call. | ||
* @param call function call to bind arguments for | ||
* @see InjectedArgumentsBinder for the injected argument subset | ||
*/ | ||
class RegularArgumentsBinder(private val call: FunctionCall<*>) : ArgumentsBinder { | ||
override fun createBindings(parameters: List<FunctionParameter<*>>) = | ||
buildMap { | ||
var encounteredNamedArgument = false | ||
|
||
call.arguments.forEachIndexed { index, argument -> | ||
// Corresponding parameter. | ||
val parameter = | ||
when { | ||
// A body parameter is always the last one in the function signature. | ||
argument.isBody -> parameters.lastOrNull() | ||
// A non-body parameter that refers to a parameter by its name. | ||
argument.isNamed -> { | ||
encounteredNamedArgument = true | ||
parameters.find { it.name == argument.name } | ||
?: throw UnresolvedParameterException(argument, call) | ||
} | ||
// Non-body, unnamed parameters follow the index and cannot appear after a named argument has been encountered. | ||
!encounteredNamedArgument -> parameters.getOrNull(index) | ||
// Unnamed arguments cannot appear after a named one. | ||
else -> throw UnnamedArgumentAfterNamedException(call) | ||
} ?: throw InvalidArgumentCountException(call) // Error if args count > params count. | ||
|
||
val value = argument.value | ||
// The type of dynamic arguments is determined. | ||
val staticArgument = | ||
when { | ||
// If the expected type is dynamic, the argument is wrapped into a dynamic value. | ||
// For instance, custom functions defined from a Quarkdown function have dynamic-type parameters. | ||
parameter.type == DynamicValue::class -> { | ||
argument.copy(expression = DynamicValue(value.unwrappedValue)) | ||
} | ||
|
||
// The value is dynamic and must be converted to a static type. | ||
value is DynamicValue -> { | ||
// The dynamic value is converted into the expected parameter type. | ||
// Throws error if the conversion could not happen. | ||
val staticValue = | ||
DynamicValueConverter(value).convertTo(parameter.type, call.context) | ||
?: throw MismatchingArgumentTypeException(call, parameter, argument) | ||
|
||
argument.copy(expression = staticValue) | ||
} | ||
|
||
// If the expected type is a string but the argument isn't, | ||
// it is automatically converted to a string. | ||
value !is StringValue && parameter.type == String::class -> { | ||
argument.copy(expression = ValueFactory.string(value.unwrappedValue.toString())) | ||
} | ||
|
||
else -> argument | ||
} | ||
|
||
// Type match check. | ||
if ( | ||
!staticArgument.value.unwrappedValue!!::class.isSubclassOf(parameter.type) && | ||
!staticArgument.value::class.isSubclassOf(parameter.type) | ||
) { | ||
throw MismatchingArgumentTypeException(call, parameter, staticArgument) | ||
} | ||
|
||
// Add link. | ||
this[parameter] = staticArgument | ||
} | ||
} | ||
} |