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,
StringJoinOps,
SequenceOps,
// RangeOps,
RangeOps,
Numeric,
// ComparableOps,
ComparableOps,
// CommonArrays,
// PlatformSpecialized,
// 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
import templates.Family.*
fun comparables(): List<GenericFunction> {
val templates = arrayListOf<GenericFunction>()
object ComparableOps : TemplateGroupBase() {
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)
only(Primitives, Generic)
only(numericPrimitives)
returns("SELF")
typeParam("T: Comparable<T>")
doc {
@@ -23,13 +43,13 @@ fun comparables(): List<GenericFunction> {
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)
only(Primitives, Generic)
only(numericPrimitives)
returns("SELF")
typeParam("T: Comparable<T>")
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)
only(Primitives, Generic)
only(PrimitiveType.Int, PrimitiveType.Long)
returns("SELF")
typeParam("T: Comparable<T>")
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)
only(Generic)
since("1.1")
returns("SELF")
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)
only(Primitives, Generic)
only(numericPrimitives)
since("1.1")
typeParam("T: Comparable<T>")
returns("T")
customReceiver("")
inline(Primitives) { Inline.Only }
receiver("")
specialFor(Primitives) { inlineOnly() }
doc {
"""
Returns the smaller of two values.
@@ -131,50 +154,56 @@ fun comparables(): List<GenericFunction> {
"""
}
// TODO: Add a note about NaN propagation for floats.
doc(Primitives) {
"""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(Primitives) {
"return Math.min(a, b)"
specialFor(Primitives) {
doc {
"""Returns the smaller of two values."""
}
// TODO: custom body for JS minOf(Long, Long)
body {
"return Math.min(a, b)"
}
if (primitive in shortIntPrimitives) {
body { "return Math.min(a.toInt(), b.toInt()).to$primitive()" }
}
}
body(Generic) {
"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)
only(Primitives, Generic)
only(numericPrimitives)
since("1.1")
typeParam("T: Comparable<T>")
returns("T")
customReceiver("")
inline(Primitives) { Inline.Only }
receiver("")
specialFor(Primitives) { inlineOnly() }
// TODO: Add a note about NaN propagation for floats.
doc {
"""
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 {
"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)
only(Generic)
since("1.1")
returns("T")
customReceiver("")
receiver("")
doc {
"""
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)
only(Generic)
since("1.1")
returns("T")
customReceiver("")
receiver("")
doc {
"""
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)"
}
}
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)
only(Primitives, Generic)
only(numericPrimitives)
since("1.1")
typeParam("T: Comparable<T>")
returns("T")
customReceiver("")
inline(Primitives) { Inline.Only }
receiver("")
specialFor(Primitives) { inlineOnly() }
doc {
"""
Returns the greater of two values.
@@ -218,49 +249,55 @@ fun comparables(): List<GenericFunction> {
"""
}
// TODO: Add a note about NaN propagation for floats.
doc(Primitives) {
"""Returns the greater of two values."""
}
bodyForTypes(Primitives, PrimitiveType.Byte, PrimitiveType.Short) { p ->
"return Math.max(a.toInt(), b.toInt()).to$p()"
}
body(Primitives) {
"return Math.max(a, b)"
specialFor(Primitives) {
doc {
"""Returns the greater of two values."""
}
body {
"return Math.max(a, b)"
}
if (primitive in shortIntPrimitives) {
body { "return Math.max(a.toInt(), b.toInt()).to$primitive()" }
}
}
body(Generic) {
"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)
only(Primitives, Generic)
only(numericPrimitives)
since("1.1")
typeParam("T: Comparable<T>")
returns("T")
customReceiver("")
inline(Primitives) { Inline.Only }
receiver("")
specialFor(Primitives) { inlineOnly() }
// TODO: Add a note about NaN propagation for floats.
doc {
"""
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 {
"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)
only(Generic)
since("1.1")
returns("T")
customReceiver("")
receiver("")
doc {
"""
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)
only(Generic)
since("1.1")
returns("T")
customReceiver("")
receiver("")
doc {
"""
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)
only(Primitives, Generic)
only(numericPrimitives)
customSignature(Generic) { "coerceIn(minimumValue: SELF?, maximumValue: SELF?)" }
specialFor(Generic) { signature("coerceIn(minimumValue: SELF?, maximumValue: SELF?)", notForSorting = true) }
typeParam("T: Comparable<T>")
returns("SELF")
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
import templates.Family.*
import templates.PrimitiveType.Companion.maxByCapacity
fun ranges(): List<GenericFunction> {
val templates = arrayListOf<GenericFunction>()
object RangeOps : TemplateGroupBase() {
val rangePrimitives = listOf(PrimitiveType.Int, PrimitiveType.Long, PrimitiveType.Char)
fun rangeElementType(fromType: PrimitiveType, toType: PrimitiveType)
private val rangePrimitives = setOf(PrimitiveType.Int, PrimitiveType.Long, PrimitiveType.Char)
private fun rangeElementType(fromType: PrimitiveType, toType: PrimitiveType)
= 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()") {
only(ProgressionsOfPrimitives)
only(rangePrimitives)
doc(ProgressionsOfPrimitives) { "Returns a progression that goes over the same range in the opposite direction with the same step." }
private val numericPrimitives = PrimitiveType.numericPrimitives
private val numericPermutations = numericPrimitives.permutations()
private val primitivePermutations = numericPermutations + (PrimitiveType.Char to PrimitiveType.Char)
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")
body(ProgressionsOfPrimitives) {
body {
"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)
only(ProgressionsOfPrimitives)
only(rangePrimitives)
doc(ProgressionsOfPrimitives) { "Returns a progression that goes over the same range with the given step." }
doc { "Returns a progression that goes over the same range with the given step." }
returns("TProgression")
body(ProgressionsOfPrimitives) {
body {
"""
checkStepIsPositive(step > 0, 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)") {
infix(true)
val f_downTo = fn("downTo(to: Primitive)").byTwoPrimitives {
include(Primitives, integralPermutations)
} builderWith { (fromType, toType) ->
sourceFile(SourceFile.Ranges)
only(Primitives)
only(fromType)
val elementType = rangeElementType(fromType, toType)
val progressionType = elementType.name + "Progression"
infix()
signature("downTo(to: $toType)")
returns(progressionType)
doc {
@@ -55,6 +77,7 @@ fun ranges(): List<GenericFunction> {
"""
}
val fromExpr = if (elementType == fromType) "this" else "this.to$elementType()"
val toExpr = if (elementType == toType) "to" else "to.to$elementType()"
val incrementExpr = when (elementType) {
@@ -64,22 +87,20 @@ fun ranges(): List<GenericFunction> {
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)
only(Primitives)
only(fromType)
infix()
signature("until(to: $toType)")
val elementType = rangeElementType(fromType, toType)
val progressionType = elementType.name + "Range"
returns(progressionType)
@@ -116,14 +137,14 @@ fun ranges(): List<GenericFunction> {
}
}
templates addAll integralPermutations.map { until(it.first, it.second) }
fun contains(rangeType: PrimitiveType, itemType: PrimitiveType) = f("contains(value: $itemType)") {
operator(true)
val f_contains = fn("contains(value: Primitive)").byTwoPrimitives {
include(Ranges, numericPermutations)
filter { _, (rangeType, itemType) -> rangeType != itemType }
} 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" }
only(Ranges)
onlyPrimitives(Ranges, rangeType)
platformName("${rangeType.name.decapitalize()}RangeContains")
returns("Boolean")
doc { "Checks if the specified [value] belongs to this range." }
@@ -135,23 +156,18 @@ fun ranges(): List<GenericFunction> {
}
}
templates addAll numericPermutations.filter { it.first != it.second }.map { contains(it.first, it.second) }
fun narrowingExactOrNull(fromType: PrimitiveType, toType: PrimitiveType) = f("to${toType}ExactOrNull()") {
val f_toPrimitiveExactOrNull = fn("to{}ExactOrNull()").byTwoPrimitives {
include(Primitives, numericPermutations)
filter { _, (fromType, toType) -> fromType.capacity > toType.capacity && toType.isIntegral() }
} builderWith { (fromType, toType) ->
check(toType.isIntegral())
visibility("internal")
sourceFile(SourceFile.Ranges)
check(toType.isIntegral())
signature("to${toType}ExactOrNull()")
returns("$toType?")
only(Primitives)
only(fromType)
body {
"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
}
}