From a486ceb602dac6cd76a1f9356112dff05ff5a716 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 24 Oct 2020 12:00:56 +0800 Subject: [PATCH] Resolution with optional defaults --- .../resolve/BuiltInCommandCallResolver.kt | 39 ++++++++++++------- .../mirai/console/command/TestCommand.kt | 7 ++-- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/resolve/BuiltInCommandCallResolver.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/resolve/BuiltInCommandCallResolver.kt index 58d420c56..0409808ac 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/resolve/BuiltInCommandCallResolver.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/resolve/BuiltInCommandCallResolver.kt @@ -8,9 +8,7 @@ import net.mamoe.mirai.console.command.parse.CommandCall import net.mamoe.mirai.console.command.parse.CommandValueArgument import net.mamoe.mirai.console.extensions.CommandCallResolverProvider import net.mamoe.mirai.console.util.ConsoleExperimentalApi -import net.mamoe.mirai.console.util.cast import net.mamoe.mirai.console.util.safeCast -import java.util.* /** * Builtin implementation of [CommandCallResolver] @@ -34,7 +32,10 @@ public object BuiltInCommandCallResolver : CommandCallResolver { private data class ResolveData( val variant: CommandSignatureVariant, val argumentAcceptances: List, - ) + val remainingParameters: List>, + ) { + val remainingOptionalCount: Int = remainingParameters.count { it.isOptional } + } private data class ArgumentAcceptanceWithIndex( val index: Int, @@ -51,15 +52,21 @@ public object BuiltInCommandCallResolver : CommandCallResolver { .mapNotNull l@{ signature -> val zipped = signature.valueParameters.zip(valueArguments) - if (signature.valueParameters.drop(zipped.size).any { !it.isOptional }) return@l null // not enough args + val remaining = signature.valueParameters.drop(zipped.size) - ResolveData(signature, zipped.mapIndexed { index, (parameter, argument) -> - val accepting = parameter.accepting(argument, context) - if (accepting.isNotAcceptable) { - return@l null // argument type not assignable - } - ArgumentAcceptanceWithIndex(index, accepting) - }) + if (remaining.any { !it.isOptional }) return@l null // not enough args + + ResolveData( + variant = signature, + argumentAcceptances = zipped.mapIndexed { index, (parameter, argument) -> + val accepting = parameter.accepting(argument, context) + if (accepting.isNotAcceptable) { + return@l null // argument type not assignable + } + ArgumentAcceptanceWithIndex(index, accepting) + }, + remainingParameters = remaining + ) } .also { result -> result.singleOrNull()?.let { return it.variant } } .takeLongestMatches() @@ -117,11 +124,13 @@ fun main() { } */ - private fun List.takeLongestMatches(): List { + private fun List.takeLongestMatches(): Collection { if (isEmpty()) return emptyList() - return associateByTo(TreeMap(Comparator.reverseOrder())) { it.variant.valueParameters.size }.let { m -> - val firstKey = m.keys.first().cast() - m.filter { it.key == firstKey }.map { it.value.cast() } + return associateWith { + it.variant.valueParameters.size - it.remainingOptionalCount * 1.001 // slightly lower priority with optional defaults. + }.let { m -> + val maxMatch = m.values.maxByOrNull { it } + m.filter { it.value == maxMatch }.keys } } } \ No newline at end of file diff --git a/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/command/TestCommand.kt b/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/command/TestCommand.kt index 382d0a617..70b65b298 100644 --- a/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/command/TestCommand.kt +++ b/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/command/TestCommand.kt @@ -164,8 +164,8 @@ internal class TestCommand { @Test fun `composite command descriptors`() { val overloads = TestCompositeCommand.overloads - assertEquals("CommandSignatureVariant(seconds: Int = ...)", overloads[0].toString()) - assertEquals("CommandSignatureVariant(target: Long, seconds: Int)", overloads[1].toString()) + assertEquals("CommandSignatureVariant(, seconds: Int = ...)", overloads[0].toString()) + assertEquals("CommandSignatureVariant(, target: Long, seconds: Int)", overloads[1].toString()) } @Test @@ -225,6 +225,7 @@ internal class TestCommand { } override fun parse(raw: MessageContent, sender: CommandSender): MyClass { + if (raw is PlainText) return parse(raw.content, sender) assertSame(image, raw) return MyClass(2) } @@ -238,7 +239,7 @@ internal class TestCommand { } composite.withRegistration { - assertEquals(333, withTesting { execute(sender, "mute 333") }.value) + assertEquals(333, withTesting { assertSuccess(execute(sender, "mute 333")) }.value) assertEquals(2, withTesting { assertSuccess( execute(sender, buildMessageChain {