From ccc3646bb83c74390319c498f4ec984deedd6ae1 Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Wed, 15 Apr 2015 22:14:27 +0300 Subject: [PATCH] Generate coerceAtLeast, coerceAtMost and coerceIn extension functions for numbers and other Comparables. --- .../stdlib/src/generated/_Comparables.kt | 305 ++++++++++++++++++ libraries/stdlib/test/numbers/CoercionTest.kt | 118 +++++++ .../src/generators/GenerateCollections.kt | 6 +- .../src/templates/Comparables.kt | 97 ++++++ .../kotlin-stdlib-gen/src/templates/Engine.kt | 40 ++- 5 files changed, 560 insertions(+), 6 deletions(-) create mode 100644 libraries/stdlib/src/generated/_Comparables.kt create mode 100644 libraries/stdlib/test/numbers/CoercionTest.kt create mode 100644 libraries/tools/kotlin-stdlib-gen/src/templates/Comparables.kt diff --git a/libraries/stdlib/src/generated/_Comparables.kt b/libraries/stdlib/src/generated/_Comparables.kt new file mode 100644 index 00000000000..50d0505092e --- /dev/null +++ b/libraries/stdlib/src/generated/_Comparables.kt @@ -0,0 +1,305 @@ +package kotlin + +// +// NOTE THIS FILE IS AUTO-GENERATED by the GenerateStandardLib.kt +// See: https://github.com/JetBrains/kotlin/tree/master/libraries/stdlib +// + +import java.util.* + +import java.util.Collections // TODO: it's temporary while we have java.util.Collections in js + +/** + * Ensures that this value is not less than the specified [minimumValue]. + * @return this value if it's greater than or equal to the [minimumValue] or the [minimumValue] otherwise. + */ +public fun > T.coerceAtLeast(minimumValue: T): T { + return if (this < minimumValue) minimumValue else this +} + +/** + * Ensures that this value is not less than the specified [minimumValue]. + * @return this value if it's greater than or equal to the [minimumValue] or the [minimumValue] otherwise. + */ +public fun Byte.coerceAtLeast(minimumValue: Byte): Byte { + return if (this < minimumValue) minimumValue else this +} + +/** + * Ensures that this value is not less than the specified [minimumValue]. + * @return this value if it's greater than or equal to the [minimumValue] or the [minimumValue] otherwise. + */ +public fun Double.coerceAtLeast(minimumValue: Double): Double { + return if (this < minimumValue) minimumValue else this +} + +/** + * Ensures that this value is not less than the specified [minimumValue]. + * @return this value if it's greater than or equal to the [minimumValue] or the [minimumValue] otherwise. + */ +public fun Float.coerceAtLeast(minimumValue: Float): Float { + return if (this < minimumValue) minimumValue else this +} + +/** + * Ensures that this value is not less than the specified [minimumValue]. + * @return this value if it's greater than or equal to the [minimumValue] or the [minimumValue] otherwise. + */ +public fun Int.coerceAtLeast(minimumValue: Int): Int { + return if (this < minimumValue) minimumValue else this +} + +/** + * Ensures that this value is not less than the specified [minimumValue]. + * @return this value if it's greater than or equal to the [minimumValue] or the [minimumValue] otherwise. + */ +public fun Long.coerceAtLeast(minimumValue: Long): Long { + return if (this < minimumValue) minimumValue else this +} + +/** + * Ensures that this value is not less than the specified [minimumValue]. + * @return this value if it's greater than or equal to the [minimumValue] or the [minimumValue] otherwise. + */ +public fun Short.coerceAtLeast(minimumValue: Short): Short { + return if (this < minimumValue) minimumValue else this +} + +/** + * Ensures that this value is not greater than the specified [maximumValue]. + * @return this value if it's greater than or equal to the [maximumValue] or the [maximumValue] otherwise. + */ +public fun > T.coerceAtMost(maximumValue: T): T { + return if (this > maximumValue) maximumValue else this +} + +/** + * Ensures that this value is not greater than the specified [maximumValue]. + * @return this value if it's greater than or equal to the [maximumValue] or the [maximumValue] otherwise. + */ +public fun Byte.coerceAtMost(maximumValue: Byte): Byte { + return if (this > maximumValue) maximumValue else this +} + +/** + * Ensures that this value is not greater than the specified [maximumValue]. + * @return this value if it's greater than or equal to the [maximumValue] or the [maximumValue] otherwise. + */ +public fun Double.coerceAtMost(maximumValue: Double): Double { + return if (this > maximumValue) maximumValue else this +} + +/** + * Ensures that this value is not greater than the specified [maximumValue]. + * @return this value if it's greater than or equal to the [maximumValue] or the [maximumValue] otherwise. + */ +public fun Float.coerceAtMost(maximumValue: Float): Float { + return if (this > maximumValue) maximumValue else this +} + +/** + * Ensures that this value is not greater than the specified [maximumValue]. + * @return this value if it's greater than or equal to the [maximumValue] or the [maximumValue] otherwise. + */ +public fun Int.coerceAtMost(maximumValue: Int): Int { + return if (this > maximumValue) maximumValue else this +} + +/** + * Ensures that this value is not greater than the specified [maximumValue]. + * @return this value if it's greater than or equal to the [maximumValue] or the [maximumValue] otherwise. + */ +public fun Long.coerceAtMost(maximumValue: Long): Long { + return if (this > maximumValue) maximumValue else this +} + +/** + * Ensures that this value is not greater than the specified [maximumValue]. + * @return this value if it's greater than or equal to the [maximumValue] or the [maximumValue] otherwise. + */ +public fun Short.coerceAtMost(maximumValue: Short): Short { + return if (this > maximumValue) maximumValue else this +} + +/** + * Ensures that this value lies in the specified range [minimumValue]..[maximumValue]. + * @return this value if it's in the range, or [minimumValue] if this value is less than [minimumValue], or [maximumValue] if this value is greater than [maximumValue]. + */ +public fun > T.coerceIn(minimumValue: T?, maximumValue: T?): T { + if (minimumValue !== null && maximumValue !== null) { + if (minimumValue > maximumValue) throw IllegalArgumentException("Cannot coerce value to an empty range: maximum $maximumValue is less than minimum $minimumValue.") + if (this < minimumValue) return minimumValue + if (this > maximumValue) return maximumValue + } + else { + if (minimumValue !== null && this < minimumValue) return minimumValue + if (maximumValue !== null && this > maximumValue) return maximumValue + } + return this +} + +/** + * Ensures that this value lies in the specified range [minimumValue]..[maximumValue]. + * @return this value if it's in the range, or [minimumValue] if this value is less than [minimumValue], or [maximumValue] if this value is greater than [maximumValue]. + */ +public fun Byte.coerceIn(minimumValue: Byte?, maximumValue: Byte?): Byte { + if (minimumValue !== null && maximumValue !== null) { + if (minimumValue > maximumValue) throw IllegalArgumentException("Cannot coerce value to an empty range: maximum $maximumValue is less than minimum $minimumValue.") + if (this < minimumValue) return minimumValue + if (this > maximumValue) return maximumValue + } + else { + if (minimumValue !== null && this < minimumValue) return minimumValue + if (maximumValue !== null && this > maximumValue) return maximumValue + } + return this +} + +/** + * Ensures that this value lies in the specified range [minimumValue]..[maximumValue]. + * @return this value if it's in the range, or [minimumValue] if this value is less than [minimumValue], or [maximumValue] if this value is greater than [maximumValue]. + */ +public fun Double.coerceIn(minimumValue: Double?, maximumValue: Double?): Double { + if (minimumValue !== null && maximumValue !== null) { + if (minimumValue > maximumValue) throw IllegalArgumentException("Cannot coerce value to an empty range: maximum $maximumValue is less than minimum $minimumValue.") + if (this < minimumValue) return minimumValue + if (this > maximumValue) return maximumValue + } + else { + if (minimumValue !== null && this < minimumValue) return minimumValue + if (maximumValue !== null && this > maximumValue) return maximumValue + } + return this +} + +/** + * Ensures that this value lies in the specified range [minimumValue]..[maximumValue]. + * @return this value if it's in the range, or [minimumValue] if this value is less than [minimumValue], or [maximumValue] if this value is greater than [maximumValue]. + */ +public fun Float.coerceIn(minimumValue: Float?, maximumValue: Float?): Float { + if (minimumValue !== null && maximumValue !== null) { + if (minimumValue > maximumValue) throw IllegalArgumentException("Cannot coerce value to an empty range: maximum $maximumValue is less than minimum $minimumValue.") + if (this < minimumValue) return minimumValue + if (this > maximumValue) return maximumValue + } + else { + if (minimumValue !== null && this < minimumValue) return minimumValue + if (maximumValue !== null && this > maximumValue) return maximumValue + } + return this +} + +/** + * Ensures that this value lies in the specified range [minimumValue]..[maximumValue]. + * @return this value if it's in the range, or [minimumValue] if this value is less than [minimumValue], or [maximumValue] if this value is greater than [maximumValue]. + */ +public fun Int.coerceIn(minimumValue: Int?, maximumValue: Int?): Int { + if (minimumValue !== null && maximumValue !== null) { + if (minimumValue > maximumValue) throw IllegalArgumentException("Cannot coerce value to an empty range: maximum $maximumValue is less than minimum $minimumValue.") + if (this < minimumValue) return minimumValue + if (this > maximumValue) return maximumValue + } + else { + if (minimumValue !== null && this < minimumValue) return minimumValue + if (maximumValue !== null && this > maximumValue) return maximumValue + } + return this +} + +/** + * Ensures that this value lies in the specified range [minimumValue]..[maximumValue]. + * @return this value if it's in the range, or [minimumValue] if this value is less than [minimumValue], or [maximumValue] if this value is greater than [maximumValue]. + */ +public fun Long.coerceIn(minimumValue: Long?, maximumValue: Long?): Long { + if (minimumValue !== null && maximumValue !== null) { + if (minimumValue > maximumValue) throw IllegalArgumentException("Cannot coerce value to an empty range: maximum $maximumValue is less than minimum $minimumValue.") + if (this < minimumValue) return minimumValue + if (this > maximumValue) return maximumValue + } + else { + if (minimumValue !== null && this < minimumValue) return minimumValue + if (maximumValue !== null && this > maximumValue) return maximumValue + } + return this +} + +/** + * Ensures that this value lies in the specified range [minimumValue]..[maximumValue]. + * @return this value if it's in the range, or [minimumValue] if this value is less than [minimumValue], or [maximumValue] if this value is greater than [maximumValue]. + */ +public fun Short.coerceIn(minimumValue: Short?, maximumValue: Short?): Short { + if (minimumValue !== null && maximumValue !== null) { + if (minimumValue > maximumValue) throw IllegalArgumentException("Cannot coerce value to an empty range: maximum $maximumValue is less than minimum $minimumValue.") + if (this < minimumValue) return minimumValue + if (this > maximumValue) return maximumValue + } + else { + if (minimumValue !== null && this < minimumValue) return minimumValue + if (maximumValue !== null && this > maximumValue) return maximumValue + } + return this +} + +/** + * Ensures that this value lies in the specified [range]. + * @return this value if it's in the [range], or range.start if this value is less than range.start, or range.end if this value is greater than range.end. + */ +public fun > T.coerceIn(range: Range): T { + if (range.isEmpty()) throw IllegalArgumentException("Cannot coerce value to an empty range: $range.") + return if (this < range.start) range.start else if (this > range.end) range.end else this +} + +/** + * Ensures that this value lies in the specified [range]. + * @return this value if it's in the [range], or range.start if this value is less than range.start, or range.end if this value is greater than range.end. + */ +public fun Byte.coerceIn(range: ByteRange): Byte { + if (range.isEmpty()) throw IllegalArgumentException("Cannot coerce value to an empty range: $range.") + return if (this < range.start) range.start else if (this > range.end) range.end else this +} + +/** + * Ensures that this value lies in the specified [range]. + * @return this value if it's in the [range], or range.start if this value is less than range.start, or range.end if this value is greater than range.end. + */ +public fun Double.coerceIn(range: DoubleRange): Double { + if (range.isEmpty()) throw IllegalArgumentException("Cannot coerce value to an empty range: $range.") + return if (this < range.start) range.start else if (this > range.end) range.end else this +} + +/** + * Ensures that this value lies in the specified [range]. + * @return this value if it's in the [range], or range.start if this value is less than range.start, or range.end if this value is greater than range.end. + */ +public fun Float.coerceIn(range: FloatRange): Float { + if (range.isEmpty()) throw IllegalArgumentException("Cannot coerce value to an empty range: $range.") + return if (this < range.start) range.start else if (this > range.end) range.end else this +} + +/** + * Ensures that this value lies in the specified [range]. + * @return this value if it's in the [range], or range.start if this value is less than range.start, or range.end if this value is greater than range.end. + */ +public fun Int.coerceIn(range: IntRange): Int { + if (range.isEmpty()) throw IllegalArgumentException("Cannot coerce value to an empty range: $range.") + return if (this < range.start) range.start else if (this > range.end) range.end else this +} + +/** + * Ensures that this value lies in the specified [range]. + * @return this value if it's in the [range], or range.start if this value is less than range.start, or range.end if this value is greater than range.end. + */ +public fun Long.coerceIn(range: LongRange): Long { + if (range.isEmpty()) throw IllegalArgumentException("Cannot coerce value to an empty range: $range.") + return if (this < range.start) range.start else if (this > range.end) range.end else this +} + +/** + * Ensures that this value lies in the specified [range]. + * @return this value if it's in the [range], or range.start if this value is less than range.start, or range.end if this value is greater than range.end. + */ +public fun Short.coerceIn(range: ShortRange): Short { + if (range.isEmpty()) throw IllegalArgumentException("Cannot coerce value to an empty range: $range.") + return if (this < range.start) range.start else if (this > range.end) range.end else this +} + diff --git a/libraries/stdlib/test/numbers/CoercionTest.kt b/libraries/stdlib/test/numbers/CoercionTest.kt new file mode 100644 index 00000000000..7c9368d8f80 --- /dev/null +++ b/libraries/stdlib/test/numbers/CoercionTest.kt @@ -0,0 +1,118 @@ +package numbers + +import org.junit.Test +import org.junit.Test as test +import kotlin.test.* + +class CoercionTest { + + fun usage() { + val n = 1 + // infix usage + val n1 = n coerceAtLeast 2 + // function usage + val n2 = n.coerceAtLeast(2) + + // infix with range + val n3 = n coerceIn 2..5 + } + + Test fun coercionsInt() { + expect(5) { 5.coerceAtLeast(1) } + expect(5) { 1.coerceAtLeast(5) } + expect(1) { 5.coerceAtMost(1) } + expect(1) { 1.coerceAtMost(5) } + + for (value in 0..10) { + expect(value) { value.coerceIn(null, null) } + val min = 2 + val max = 5 + val range = min..max + expect(value.coerceAtLeast(min)) { value.coerceIn(min, null) } + expect(value.coerceAtMost(max)) { value.coerceIn(null, max) } + expect(value.coerceAtLeast(min).coerceAtMost(max)) { value.coerceIn(min, max) } + expect(value.coerceAtMost(max).coerceAtLeast(min)) { value coerceIn range } + assertTrue((value coerceIn range) in range) + } + + fails { 1.coerceIn(1, 0) } + fails { 1.coerceIn(1..0) } + } + + Test fun coercionsLong() { + expect(5L) { 5L.coerceAtLeast(1L) } + expect(5L) { 1L.coerceAtLeast(5L) } + expect(1L) { 5L.coerceAtMost(1L) } + expect(1L) { 1L.coerceAtMost(5L) } + + for (value in 0L..10L) { + expect(value) { value.coerceIn(null, null) } + val min = 2L + val max = 5L + val range = min..max + expect(value.coerceAtLeast(min)) { value.coerceIn(min, null) } + expect(value.coerceAtMost(max)) { value.coerceIn(null, max) } + expect(value.coerceAtLeast(min).coerceAtMost(max)) { value.coerceIn(min, max) } + expect(value.coerceAtMost(max).coerceAtLeast(min)) { value coerceIn range } + assertTrue((value coerceIn range) in range) + } + + fails { 1L.coerceIn(1L, 0L) } + fails { 1L.coerceIn(1L..0L) } + + } + + Test fun coercionsDouble() { + expect(5.0) { 5.0.coerceAtLeast(1.0) } + expect(5.0) { 1.0.coerceAtLeast(5.0) } + expect(1.0) { 5.0.coerceAtMost(1.0) } + expect(1.0) { 1.0.coerceAtMost(5.0) } + + for (value in 0.0..10.0) { + expect(value) { value.coerceIn(null, null) } + val min = 2.0 + val max = 5.0 + val range = min..max + expect(value.coerceAtLeast(min)) { value.coerceIn(min, null) } + expect(value.coerceAtMost(max)) { value.coerceIn(null, max) } + expect(value.coerceAtLeast(min).coerceAtMost(max)) { value.coerceIn(min, max) } + expect(value.coerceAtMost(max).coerceAtLeast(min)) { value coerceIn range } + assertTrue((value coerceIn range) in range) + } + + fails { 1.0.coerceIn(1.0, 0.0) } + fails { 1.0.coerceIn(1.0..0.0) } + } + + Test fun coercionsComparable() { + val v = 0..10 map { ComparableNumber(it) } + + expect(5) { v[5].coerceAtLeast(v[1]).value } + expect(5) { v[1].coerceAtLeast(v[5]).value } + expect(v[5]) { v[5].coerceAtLeast(ComparableNumber(5)) } + + expect(1) { v[5].coerceAtMost(v[1]).value } + expect(1) { v[1].coerceAtMost(v[5]).value } + expect(v[1]) { v[1].coerceAtMost(ComparableNumber(1)) } + + for (value in v) { + expect(value) { value.coerceIn(null, null) } + val min = v[2] + val max = v[5] + val range = min..max + expect(value.coerceAtLeast(min)) { value.coerceIn(min, null) } + expect(value.coerceAtMost(max)) { value.coerceIn(null, max) } + expect(value.coerceAtLeast(min).coerceAtMost(max)) { value.coerceIn(min, max) } + expect(value.coerceAtMost(max).coerceAtLeast(min)) { value coerceIn range } + assertTrue((value coerceIn range) in range) + } + + fails { v[1].coerceIn(v[1], v[0]) } + fails { v[1].coerceIn(v[1]..v[0]) } + } +} + +private class ComparableNumber(public val value: Int) : Comparable { + override fun compareTo(other: ComparableNumber): Int = this.value - other.value + override fun toString(): String = "CV$value" +} \ No newline at end of file diff --git a/libraries/tools/kotlin-stdlib-gen/src/generators/GenerateCollections.kt b/libraries/tools/kotlin-stdlib-gen/src/generators/GenerateCollections.kt index 6006acd5dc9..299a64d14c4 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/generators/GenerateCollections.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/generators/GenerateCollections.kt @@ -29,15 +29,17 @@ fun generateCollectionsAPI(outDir: File) { build(builder, Sequences, numeric) } - for (numeric in listOf(PrimitiveType.Int, PrimitiveType.Long, PrimitiveType.Byte, PrimitiveType.Short, PrimitiveType.Double, PrimitiveType.Float)) { + for (numeric in numericPrimitives) { build(builder, ArraysOfObjects, numeric) build(builder, ArraysOfPrimitives, numeric) } builder.toString() } + comparables().writeTo(File(outDir, "_Comparables.kt")) { build() } + } fun generateCollectionsJsAPI(outDir: File) { specialJS().writeTo(File(outDir, "kotlin_special.kt")) { build() } -} \ No newline at end of file +} diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/Comparables.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/Comparables.kt new file mode 100644 index 00000000000..fda97e8d062 --- /dev/null +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/Comparables.kt @@ -0,0 +1,97 @@ +package templates + +import templates.Family.* + +fun comparables(): List { + val templates = arrayListOf() + + templates add f("coerceAtLeast(minimumValue: SELF)") { + only(Primitives, Generic) + only(*numericPrimitives) + returns("SELF") + typeParam("T: Comparable") + doc { + """ + Ensures that this value is not less than the specified [minimumValue]. + + @return this value if it's greater than or equal to the [minimumValue] or the [minimumValue] otherwise. + """ + } + body { + """ + return if (this < minimumValue) minimumValue else this + """ + } + + } + + templates add f("coerceAtMost(maximumValue: SELF)") { + only(Primitives, Generic) + only(*numericPrimitives) + returns("SELF") + typeParam("T: Comparable") + doc { + """ + Ensures that this value is not greater than the specified [maximumValue]. + + @return this value if it's greater than or equal to the [maximumValue] or the [maximumValue] otherwise. + """ + } + body { + """ + return if (this > maximumValue) maximumValue else this + """ + } + } + + templates add f("coerceIn(range: TRange)") { + only(Primitives, Generic) + only(*numericPrimitives) + returns("SELF") + typeParam("T: Comparable") + doc { + """ + Ensures that this value lies in the specified [range]. + + @return this value if it's in the [range], or range.start if this value is less than range.start, or range.end if this value is greater than range.end. + """ + } + body { + """ + if (range.isEmpty()) throw IllegalArgumentException("Cannot coerce value to an empty range: ${'$'}range.") + return if (this < range.start) range.start else if (this > range.end) range.end else this + """ + } + } + + templates add f("coerceIn(minimumValue: SELF?, maximumValue: SELF?)") { + only(Primitives, Generic) + only(*numericPrimitives) + returns("SELF") + typeParam("T: Comparable") + doc { + """ + Ensures that this value lies in the specified range [minimumValue]..[maximumValue]. + + @return this value if it's in the range, or [minimumValue] if this value is less than [minimumValue], or [maximumValue] if this value is greater than [maximumValue]. + """ + } + body { + """ + if (minimumValue !== null && maximumValue !== null) { + if (minimumValue > maximumValue) throw IllegalArgumentException("Cannot coerce value to an empty range: maximum ${'$'}maximumValue is less than minimum ${'$'}minimumValue.") + if (this < minimumValue) return minimumValue + if (this > maximumValue) return maximumValue + } + else { + if (minimumValue !== null && this < minimumValue) return minimumValue + if (maximumValue !== null && this > maximumValue) return maximumValue + } + return this + """ + } + + } + + return templates +} \ No newline at end of file diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/Engine.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/Engine.kt index 88dc1ae5074..cf8e60f69db 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/Engine.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/Engine.kt @@ -18,6 +18,8 @@ enum class Family { Strings RangesOfPrimitives ProgressionsOfPrimitives + Primitives + Generic } enum class PrimitiveType(val name: String) { @@ -32,8 +34,11 @@ enum class PrimitiveType(val name: String) { } + class GenericFunction(val signature: String, val keyword: String = "fun") : Comparable { val defaultFamilies = array(Iterables, Sequences, ArraysOfObjects, ArraysOfPrimitives, Strings) + val defaultPrimitives = PrimitiveType.values() + val numericPrimitives = array(PrimitiveType.Int, PrimitiveType.Long, PrimitiveType.Byte, PrimitiveType.Short, PrimitiveType.Double, PrimitiveType.Float) var toNullableT: Boolean = false @@ -42,7 +47,7 @@ class GenericFunction(val signature: String, val keyword: String = "fun") : Comp val inlineFamilies = HashMap() val buildFamilies = HashSet(defaultFamilies.toList()) - private val buildPrimitives = HashSet(PrimitiveType.values().toList()) + private val buildPrimitives = HashSet(defaultPrimitives.toList()) var deprecate: String = "" val deprecates = hashMapOf() @@ -136,6 +141,11 @@ class GenericFunction(val signature: String, val keyword: String = "fun") : Comp buildFamilies.addAll(families.toList()) } + fun only(vararg primitives: PrimitiveType) { + buildPrimitives.clear() + buildPrimitives.addAll(primitives.toList()) + } + fun include(vararg families: Family) { buildFamilies.addAll(families.toList()) } @@ -159,7 +169,7 @@ class GenericFunction(val signature: String, val keyword: String = "fun") : Comp } fun build(builder: StringBuilder, f: Family) { - if (f == ArraysOfPrimitives || f == RangesOfPrimitives || f == ProgressionsOfPrimitives) { + if (f == ArraysOfPrimitives || f == RangesOfPrimitives || f == ProgressionsOfPrimitives || f == Primitives) { for (primitive in buildPrimitives.sortBy { it.name() }) build(builder, f, primitive) } else { @@ -202,6 +212,8 @@ class GenericFunction(val signature: String, val keyword: String = "fun") : Comp ArraysOfPrimitives -> primitive?.let { it.name() + "Array" } ?: throw IllegalArgumentException("Primitive array should specify primitive type") RangesOfPrimitives -> primitive?.let { it.name() + "Range" } ?: throw IllegalArgumentException("Primitive range should specify primitive type") ProgressionsOfPrimitives -> primitive?.let { it.name() + "Progression" } ?: throw IllegalArgumentException("Primitive progression should specify primitive type") + Primitives -> primitive?.let { it.name } ?: throw IllegalArgumentException("Primitive should specify primitive type") + Generic -> "T" else -> throw IllegalStateException("Invalid family") } @@ -246,12 +258,24 @@ class GenericFunction(val signature: String, val keyword: String = "fun") : Comp } "T" -> { when (f) { + Generic -> "T" Strings -> "Char" Maps -> "Map.Entry" else -> primitive?.name() ?: token } } - "TProgression" -> primitive!!.name + "Progression" + "TRange" -> { + when (f) { + Generic -> "Range" + else -> primitive!!.name + "Range" + } + } + "TProgression" -> { + when (f) { + Generic -> "Progression" + else -> primitive!!.name + "Progression" + } + } else -> token }) } @@ -260,8 +284,16 @@ class GenericFunction(val signature: String, val keyword: String = "fun") : Comp } fun effectiveTypeParams(): List { + // TODO: Model for type parameter val types = ArrayList(typeParams) - if (primitive == null && f != Strings) { + if (f == Generic) { + // ensure type parameter T, if it's not added to typeParams before + if (!types.any { it == "T" || it.startsWith("T:")}) { + types.add("T") + } + return types + } + else if (primitive == null && f != Strings) { val implicitTypeParameters = receiver.dropWhile { it != '<' }.drop(1).filterNot { it == ' ' }.takeWhile { it != '>' }.split(",") for (implicit in implicitTypeParameters.reverse()) { if (implicit != "*" && !types.any { it.startsWith(implicit) || it.startsWith("reified " + implicit) }) {