Rewrite range operations in new DSL

This commit is contained in:
Ilya Gorbunov
2017-11-07 16:23:46 +03:00
parent c3b894f041
commit 3b6c5880e2
3 changed files with 184 additions and 130 deletions
@@ -24,9 +24,9 @@ fun main(args: Array<String>) {
Generators, Generators,
StringJoinOps, StringJoinOps,
SequenceOps, SequenceOps,
// RangeOps, RangeOps,
Numeric, Numeric,
// ComparableOps, ComparableOps,
// CommonArrays, // CommonArrays,
// PlatformSpecialized, // PlatformSpecialized,
// PlatformSpecializedJS, // PlatformSpecializedJS,
@@ -1,14 +1,34 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package templates package templates
import templates.Family.* import templates.Family.*
fun comparables(): List<GenericFunction> { object ComparableOps : TemplateGroupBase() {
val templates = arrayListOf<GenericFunction>()
templates add f("coerceAtLeast(minimumValue: SELF)") { private val numericPrimitives = PrimitiveType.numericPrimitives.sortedBy { it.capacity }.toSet()
private val intPrimitives = setOf(PrimitiveType.Int, PrimitiveType.Long)
private val shortIntPrimitives = setOf(PrimitiveType.Byte, PrimitiveType.Short)
val f_coerceAtLeast = fn("coerceAtLeast(minimumValue: SELF)") {
include(Generic)
include(Primitives, numericPrimitives)
} builder {
sourceFile(SourceFile.Ranges) sourceFile(SourceFile.Ranges)
only(Primitives, Generic)
only(numericPrimitives)
returns("SELF") returns("SELF")
typeParam("T: Comparable<T>") typeParam("T: Comparable<T>")
doc { doc {
@@ -23,13 +43,13 @@ fun comparables(): List<GenericFunction> {
return if (this < minimumValue) minimumValue else this return if (this < minimumValue) minimumValue else this
""" """
} }
} }
templates add f("coerceAtMost(maximumValue: SELF)") { val f_coerceAtMost = fn("coerceAtMost(maximumValue: SELF)") {
include(Generic)
include(Primitives, numericPrimitives)
} builder {
sourceFile(SourceFile.Ranges) sourceFile(SourceFile.Ranges)
only(Primitives, Generic)
only(numericPrimitives)
returns("SELF") returns("SELF")
typeParam("T: Comparable<T>") typeParam("T: Comparable<T>")
doc { doc {
@@ -46,10 +66,11 @@ fun comparables(): List<GenericFunction> {
} }
} }
templates add f("coerceIn(range: ClosedRange<T>)") { val f_coerceIn_range_primitive = fn("coerceIn(range: ClosedRange<T>)") {
include(Generic)
include(Primitives, intPrimitives)
} builder {
sourceFile(SourceFile.Ranges) sourceFile(SourceFile.Ranges)
only(Primitives, Generic)
only(PrimitiveType.Int, PrimitiveType.Long)
returns("SELF") returns("SELF")
typeParam("T: Comparable<T>") typeParam("T: Comparable<T>")
doc { doc {
@@ -87,9 +108,10 @@ fun comparables(): List<GenericFunction> {
} }
} }
templates add f("coerceIn(range: ClosedFloatingPointRange<T>)") { val f_coerceIn_fpRange = fn("coerceIn(range: ClosedFloatingPointRange<T>)") {
include(Generic)
} builder {
sourceFile(SourceFile.Ranges) sourceFile(SourceFile.Ranges)
only(Generic)
since("1.1") since("1.1")
returns("SELF") returns("SELF")
typeParam("T: Comparable<T>") typeParam("T: Comparable<T>")
@@ -115,15 +137,16 @@ fun comparables(): List<GenericFunction> {
} }
templates add f("minOf(a: T, b: T)") { val f_minOf_2 = fn("minOf(a: T, b: T)") {
include(Generic)
include(Primitives, numericPrimitives)
} builder {
sourceFile(SourceFile.Comparisons) sourceFile(SourceFile.Comparisons)
only(Primitives, Generic)
only(numericPrimitives)
since("1.1") since("1.1")
typeParam("T: Comparable<T>") typeParam("T: Comparable<T>")
returns("T") returns("T")
customReceiver("") receiver("")
inline(Primitives) { Inline.Only } specialFor(Primitives) { inlineOnly() }
doc { doc {
""" """
Returns the smaller of two values. Returns the smaller of two values.
@@ -131,50 +154,56 @@ fun comparables(): List<GenericFunction> {
""" """
} }
// TODO: Add a note about NaN propagation for floats. // TODO: Add a note about NaN propagation for floats.
doc(Primitives) { specialFor(Primitives) {
"""Returns the smaller of two values.""" doc {
} """Returns the smaller of two values."""
bodyForTypes(Primitives, PrimitiveType.Byte, PrimitiveType.Short) { p -> }
"return Math.min(a.toInt(), b.toInt()).to$p()" // TODO: custom body for JS minOf(Long, Long)
} body {
// TODO: custom body for JS minOf(Long, Long) "return Math.min(a, b)"
body(Primitives) { }
"return Math.min(a, b)" if (primitive in shortIntPrimitives) {
body { "return Math.min(a.toInt(), b.toInt()).to$primitive()" }
}
} }
body(Generic) { body(Generic) {
"return if (a <= b) a else b" "return if (a <= b) a else b"
} }
} }
templates add f("minOf(a: T, b: T, c: T)") { val f_minOf = fn("minOf(a: T, b: T, c: T)") {
include(Generic)
include(Primitives, numericPrimitives)
} builder {
sourceFile(SourceFile.Comparisons) sourceFile(SourceFile.Comparisons)
only(Primitives, Generic)
only(numericPrimitives)
since("1.1") since("1.1")
typeParam("T: Comparable<T>") typeParam("T: Comparable<T>")
returns("T") returns("T")
customReceiver("") receiver("")
inline(Primitives) { Inline.Only } specialFor(Primitives) { inlineOnly() }
// TODO: Add a note about NaN propagation for floats. // TODO: Add a note about NaN propagation for floats.
doc { doc {
""" """
Returns the smaller of three values. Returns the smaller of three values.
""" """
} }
bodyForTypes(Primitives, PrimitiveType.Byte, PrimitiveType.Short) { p ->
"return Math.min(a.toInt(), Math.min(b.toInt(), c.toInt())).to$p()"
}
body { body {
"return minOf(a, minOf(b, c))" "return minOf(a, minOf(b, c))"
} }
specialFor(Primitives) {
if (primitive in shortIntPrimitives) {
body { "return Math.min(a.toInt(), Math.min(b.toInt(), c.toInt())).to$primitive()" }
}
}
} }
templates add f("minOf(a: T, b: T, comparator: Comparator<in T>)") { val f_minOf_2_comparator = fn("minOf(a: T, b: T, comparator: Comparator<in T>)") {
include(Generic)
} builder {
sourceFile(SourceFile.Comparisons) sourceFile(SourceFile.Comparisons)
only(Generic)
since("1.1") since("1.1")
returns("T") returns("T")
customReceiver("") receiver("")
doc { doc {
""" """
Returns the smaller of two values according to the order specified by the given [comparator]. Returns the smaller of two values according to the order specified by the given [comparator].
@@ -186,12 +215,13 @@ fun comparables(): List<GenericFunction> {
} }
} }
templates add f("minOf(a: T, b: T, c: T, comparator: Comparator<in T>)") { val f_minOf_3_comparator = fn("minOf(a: T, b: T, c: T, comparator: Comparator<in T>)") {
include(Generic)
} builder {
sourceFile(SourceFile.Comparisons) sourceFile(SourceFile.Comparisons)
only(Generic)
since("1.1") since("1.1")
returns("T") returns("T")
customReceiver("") receiver("")
doc { doc {
""" """
Returns the smaller of three values according to the order specified by the given [comparator]. Returns the smaller of three values according to the order specified by the given [comparator].
@@ -201,16 +231,17 @@ fun comparables(): List<GenericFunction> {
"return minOf(a, minOf(b, c, comparator), comparator)" "return minOf(a, minOf(b, c, comparator), comparator)"
} }
} }
templates add f("maxOf(a: T, b: T)") { val f_maxOf_2 = fn("maxOf(a: T, b: T)") {
include(Generic)
include(Primitives, numericPrimitives)
} builder {
sourceFile(SourceFile.Comparisons) sourceFile(SourceFile.Comparisons)
only(Primitives, Generic)
only(numericPrimitives)
since("1.1") since("1.1")
typeParam("T: Comparable<T>") typeParam("T: Comparable<T>")
returns("T") returns("T")
customReceiver("") receiver("")
inline(Primitives) { Inline.Only } specialFor(Primitives) { inlineOnly() }
doc { doc {
""" """
Returns the greater of two values. Returns the greater of two values.
@@ -218,49 +249,55 @@ fun comparables(): List<GenericFunction> {
""" """
} }
// TODO: Add a note about NaN propagation for floats. // TODO: Add a note about NaN propagation for floats.
doc(Primitives) { specialFor(Primitives) {
"""Returns the greater of two values.""" doc {
} """Returns the greater of two values."""
bodyForTypes(Primitives, PrimitiveType.Byte, PrimitiveType.Short) { p -> }
"return Math.max(a.toInt(), b.toInt()).to$p()" body {
} "return Math.max(a, b)"
body(Primitives) { }
"return Math.max(a, b)" if (primitive in shortIntPrimitives) {
body { "return Math.max(a.toInt(), b.toInt()).to$primitive()" }
}
} }
body(Generic) { body(Generic) {
"return if (a >= b) a else b" "return if (a >= b) a else b"
} }
} }
templates add f("maxOf(a: T, b: T, c: T)") { val f_maxOf_3 = fn("maxOf(a: T, b: T, c: T)") {
include(Generic)
include(Primitives, numericPrimitives)
} builder {
sourceFile(SourceFile.Comparisons) sourceFile(SourceFile.Comparisons)
only(Primitives, Generic)
only(numericPrimitives)
since("1.1") since("1.1")
typeParam("T: Comparable<T>") typeParam("T: Comparable<T>")
returns("T") returns("T")
customReceiver("") receiver("")
inline(Primitives) { Inline.Only } specialFor(Primitives) { inlineOnly() }
// TODO: Add a note about NaN propagation for floats. // TODO: Add a note about NaN propagation for floats.
doc { doc {
""" """
Returns the greater of three values. Returns the greater of three values.
""" """
} }
bodyForTypes(Primitives, PrimitiveType.Byte, PrimitiveType.Short) { p ->
"return Math.max(a.toInt(), Math.max(b.toInt(), c.toInt())).to$p()"
}
body { body {
"return maxOf(a, maxOf(b, c))" "return maxOf(a, maxOf(b, c))"
} }
specialFor(Primitives) {
if (primitive in shortIntPrimitives) {
body { "return Math.max(a.toInt(), Math.max(b.toInt(), c.toInt())).to$primitive()" }
}
}
} }
templates add f("maxOf(a: T, b: T, comparator: Comparator<in T>)") { val f_maxOf_2_comparator = fn("maxOf(a: T, b: T, comparator: Comparator<in T>)") {
include(Generic)
} builder {
sourceFile(SourceFile.Comparisons) sourceFile(SourceFile.Comparisons)
only(Generic)
since("1.1") since("1.1")
returns("T") returns("T")
customReceiver("") receiver("")
doc { doc {
""" """
Returns the greater of two values according to the order specified by the given [comparator]. Returns the greater of two values according to the order specified by the given [comparator].
@@ -272,12 +309,13 @@ fun comparables(): List<GenericFunction> {
} }
} }
templates add f("maxOf(a: T, b: T, c: T, comparator: Comparator<in T>)") { val f_maxOf_3_comparator = fn("maxOf(a: T, b: T, c: T, comparator: Comparator<in T>)") {
include(Generic)
} builder {
sourceFile(SourceFile.Comparisons) sourceFile(SourceFile.Comparisons)
only(Generic)
since("1.1") since("1.1")
returns("T") returns("T")
customReceiver("") receiver("")
doc { doc {
""" """
Returns the greater of three values according to the order specified by the given [comparator]. Returns the greater of three values according to the order specified by the given [comparator].
@@ -289,11 +327,13 @@ fun comparables(): List<GenericFunction> {
} }
templates add f("coerceIn(minimumValue: SELF, maximumValue: SELF)") { val f_coerceIn_min_max = fn("coerceIn(minimumValue: SELF, maximumValue: SELF)") {
include(Generic)
include(Primitives, numericPrimitives)
} builder {
sourceFile(SourceFile.Ranges) sourceFile(SourceFile.Ranges)
only(Primitives, Generic)
only(numericPrimitives) specialFor(Generic) { signature("coerceIn(minimumValue: SELF?, maximumValue: SELF?)", notForSorting = true) }
customSignature(Generic) { "coerceIn(minimumValue: SELF?, maximumValue: SELF?)" }
typeParam("T: Comparable<T>") typeParam("T: Comparable<T>")
returns("SELF") returns("SELF")
doc { doc {
@@ -326,6 +366,4 @@ fun comparables(): List<GenericFunction> {
""" """
} }
} }
}
return templates
}
@@ -1,35 +1,55 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package templates package templates
import templates.Family.* import templates.Family.*
import templates.PrimitiveType.Companion.maxByCapacity import templates.PrimitiveType.Companion.maxByCapacity
fun ranges(): List<GenericFunction> { object RangeOps : TemplateGroupBase() {
val templates = arrayListOf<GenericFunction>()
val rangePrimitives = listOf(PrimitiveType.Int, PrimitiveType.Long, PrimitiveType.Char) private val rangePrimitives = setOf(PrimitiveType.Int, PrimitiveType.Long, PrimitiveType.Char)
fun rangeElementType(fromType: PrimitiveType, toType: PrimitiveType) private fun rangeElementType(fromType: PrimitiveType, toType: PrimitiveType)
= maxByCapacity(fromType, toType).let { if (it == PrimitiveType.Char) it else maxByCapacity(it, PrimitiveType.Int) } = maxByCapacity(fromType, toType).let { if (it == PrimitiveType.Char) it else maxByCapacity(it, PrimitiveType.Int) }
fun <T> Collection<T>.permutations(): List<Pair<T, T>> = flatMap { a -> map { b -> a to b } } private fun <T> Collection<T>.permutations(): List<Pair<T, T>> = flatMap { a -> map { b -> a to b } }
templates add f("reversed()") { private val numericPrimitives = PrimitiveType.numericPrimitives
only(ProgressionsOfPrimitives) private val numericPermutations = numericPrimitives.permutations()
only(rangePrimitives) private val primitivePermutations = numericPermutations + (PrimitiveType.Char to PrimitiveType.Char)
doc(ProgressionsOfPrimitives) { "Returns a progression that goes over the same range in the opposite direction with the same step." } private val integralPermutations = primitivePermutations.filter { it.first.isIntegral() && it.second.isIntegral() }
val f_reversed = fn("reversed()") {
include(ProgressionsOfPrimitives, rangePrimitives)
} builder {
doc { "Returns a progression that goes over the same range in the opposite direction with the same step." }
returns("TProgression") returns("TProgression")
body(ProgressionsOfPrimitives) { body {
"return TProgression.fromClosedRange(last, first, -step)" "return TProgression.fromClosedRange(last, first, -step)"
} }
} }
templates add f("step(step: SUM)") { val f_step = fn("step(step: SUM)") {
include(ProgressionsOfPrimitives, rangePrimitives)
} builder {
infix(true) infix(true)
doc { "Returns a progression that goes over the same range with the given step." }
only(ProgressionsOfPrimitives)
only(rangePrimitives)
doc(ProgressionsOfPrimitives) { "Returns a progression that goes over the same range with the given step." }
returns("TProgression") returns("TProgression")
body(ProgressionsOfPrimitives) { body {
""" """
checkStepIsPositive(step > 0, step) checkStepIsPositive(step > 0, step)
return TProgression.fromClosedRange(first, last, if (this.step > 0) step else -step) return TProgression.fromClosedRange(first, last, if (this.step > 0) step else -step)
@@ -37,14 +57,16 @@ fun ranges(): List<GenericFunction> {
} }
} }
fun downTo(fromType: PrimitiveType, toType: PrimitiveType) = f("downTo(to: $toType)") { val f_downTo = fn("downTo(to: Primitive)").byTwoPrimitives {
infix(true) include(Primitives, integralPermutations)
} builderWith { (fromType, toType) ->
sourceFile(SourceFile.Ranges) sourceFile(SourceFile.Ranges)
only(Primitives)
only(fromType)
val elementType = rangeElementType(fromType, toType) val elementType = rangeElementType(fromType, toType)
val progressionType = elementType.name + "Progression" val progressionType = elementType.name + "Progression"
infix()
signature("downTo(to: $toType)")
returns(progressionType) returns(progressionType)
doc { doc {
@@ -55,6 +77,7 @@ fun ranges(): List<GenericFunction> {
""" """
} }
val fromExpr = if (elementType == fromType) "this" else "this.to$elementType()" val fromExpr = if (elementType == fromType) "this" else "this.to$elementType()"
val toExpr = if (elementType == toType) "to" else "to.to$elementType()" val toExpr = if (elementType == toType) "to" else "to.to$elementType()"
val incrementExpr = when (elementType) { val incrementExpr = when (elementType) {
@@ -64,22 +87,20 @@ fun ranges(): List<GenericFunction> {
else -> "-1" else -> "-1"
} }
body { "return $progressionType.fromClosedRange($fromExpr, $toExpr, $incrementExpr)" } body {
"return $progressionType.fromClosedRange($fromExpr, $toExpr, $incrementExpr)"
}
} }
val numericPrimitives = PrimitiveType.numericPrimitives
val numericPermutations = numericPrimitives.permutations()
val primitivePermutations = numericPermutations + (PrimitiveType.Char to PrimitiveType.Char)
val integralPermutations = primitivePermutations.filter { it.first.isIntegral() && it.second.isIntegral() }
templates addAll integralPermutations.map { downTo(it.first, it.second) }
fun until(fromType: PrimitiveType, toType: PrimitiveType) = f("until(to: $toType)") {
infix(true)
val f_until = fn("until(to: Primitive)").byTwoPrimitives {
include(Primitives, integralPermutations)
} builderWith { (fromType, toType) ->
sourceFile(SourceFile.Ranges) sourceFile(SourceFile.Ranges)
only(Primitives)
only(fromType) infix()
signature("until(to: $toType)")
val elementType = rangeElementType(fromType, toType) val elementType = rangeElementType(fromType, toType)
val progressionType = elementType.name + "Range" val progressionType = elementType.name + "Range"
returns(progressionType) returns(progressionType)
@@ -116,14 +137,14 @@ fun ranges(): List<GenericFunction> {
} }
} }
templates addAll integralPermutations.map { until(it.first, it.second) } val f_contains = fn("contains(value: Primitive)").byTwoPrimitives {
include(Ranges, numericPermutations)
fun contains(rangeType: PrimitiveType, itemType: PrimitiveType) = f("contains(value: $itemType)") { filter { _, (rangeType, itemType) -> rangeType != itemType }
operator(true) } builderWith { (rangeType, itemType) ->
operator()
signature("contains(value: $itemType)")
check(rangeType.isNumeric() == itemType.isNumeric()) { "Required rangeType and itemType both to be numeric or both not, got: $rangeType, $itemType" } check(rangeType.isNumeric() == itemType.isNumeric()) { "Required rangeType and itemType both to be numeric or both not, got: $rangeType, $itemType" }
only(Ranges)
onlyPrimitives(Ranges, rangeType)
platformName("${rangeType.name.decapitalize()}RangeContains") platformName("${rangeType.name.decapitalize()}RangeContains")
returns("Boolean") returns("Boolean")
doc { "Checks if the specified [value] belongs to this range." } doc { "Checks if the specified [value] belongs to this range." }
@@ -135,23 +156,18 @@ fun ranges(): List<GenericFunction> {
} }
} }
val f_toPrimitiveExactOrNull = fn("to{}ExactOrNull()").byTwoPrimitives {
templates addAll numericPermutations.filter { it.first != it.second }.map { contains(it.first, it.second) } include(Primitives, numericPermutations)
filter { _, (fromType, toType) -> fromType.capacity > toType.capacity && toType.isIntegral() }
fun narrowingExactOrNull(fromType: PrimitiveType, toType: PrimitiveType) = f("to${toType}ExactOrNull()") { } builderWith { (fromType, toType) ->
check(toType.isIntegral())
visibility("internal") visibility("internal")
sourceFile(SourceFile.Ranges) sourceFile(SourceFile.Ranges)
check(toType.isIntegral())
signature("to${toType}ExactOrNull()")
returns("$toType?") returns("$toType?")
only(Primitives)
only(fromType)
body { body {
"return if (this in $toType.MIN_VALUE.to$fromType()..$toType.MAX_VALUE.to$fromType()) this.to$toType() else null" "return if (this in $toType.MIN_VALUE.to$fromType()..$toType.MAX_VALUE.to$fromType()) this.to$toType() else null"
} }
} }
}
templates addAll numericPermutations.filter { it.first.capacity > it.second.capacity && it.second.isIntegral() }.map { narrowingExactOrNull(it.first, it.second) }
return templates
}