Files
kotlin-fork/libraries/tools/kotlin-stdlib-gen/src/templates/Engine.kt
T

435 lines
17 KiB
Kotlin

package templates
import generators.SourceFile
import templates.Family.*
import templates.Family.Collections
import java.io.StringReader
import java.util.*
import kotlin.properties.Delegates
enum class Family {
Sequences,
Iterables,
Collections,
Lists,
Maps,
Sets,
ArraysOfObjects,
ArraysOfPrimitives,
InvariantArraysOfObjects,
Strings,
Ranges,
RangesOfPrimitives,
ProgressionsOfPrimitives,
Primitives,
Generic;
val isPrimitiveSpecialization: Boolean by lazy { this in primitiveSpecializations }
companion object {
val primitiveSpecializations = setOf(ArraysOfPrimitives, RangesOfPrimitives, ProgressionsOfPrimitives, Primitives)
val defaultFamilies = setOf(Iterables, Sequences, ArraysOfObjects, ArraysOfPrimitives, Strings)
}
}
enum class PrimitiveType {
Boolean,
Byte,
Char,
Short,
Int,
Long,
Float,
Double;
companion object {
val defaultPrimitives = PrimitiveType.values().toSet()
val numericPrimitives = setOf(Int, Long, Byte, Short, Double, Float)
val integralPrimitives = setOf(Int, Long, Byte, Short, Char)
val descendingByDomainCapacity = listOf(Double, Float, Long, Int, Short, Char, Byte)
fun maxByCapacity(fromType: PrimitiveType, toType: PrimitiveType): PrimitiveType = descendingByDomainCapacity.first { it == fromType || it == toType }
}
}
fun PrimitiveType.isIntegral(): Boolean = this in PrimitiveType.integralPrimitives
fun PrimitiveType.isNumeric(): Boolean = this in PrimitiveType.numericPrimitives
data class Deprecation(val message: String, val replaceWith: String? = null, val level: DeprecationLevel = DeprecationLevel.WARNING)
class ConcreteFunction(val textBuilder: (Appendable) -> Unit, val sourceFile: SourceFile)
class GenericFunction(val signature: String, val keyword: String = "fun") {
open class SpecializedProperty<TKey: Any, TValue : Any>() {
private val valueBuilders = HashMap<TKey?, ((TKey) -> TValue)>()
operator fun get(key: TKey): TValue? = (valueBuilders[key] ?: valueBuilders[null] ?: null)?.let { it(key) }
operator fun set(keys: Collection<TKey>, value: (TKey)->TValue) {
if (keys.isEmpty())
valueBuilders[null] = value;
else
for (key in keys) {
valueBuilders[key] = value
onKeySet(key)
}
}
operator fun invoke(vararg keys: TKey, valueBuilder: (TKey) -> TValue) = set(keys.asList(), valueBuilder)
operator fun invoke(value: TValue, vararg keys: TKey) = set(keys.asList(), { value })
protected open fun onKeySet(key: TKey) {}
}
open class FamilyProperty<TValue: Any>() : SpecializedProperty<Family, TValue>()
open class PrimitiveProperty<TValue: Any>() : SpecializedProperty<PrimitiveType, TValue>()
class DeprecationProperty() : FamilyProperty<Deprecation>() {
operator fun invoke(value: String, vararg keys: Family) = set(keys.asList(), { Deprecation(value) })
}
val defaultFamilies = Family.defaultFamilies
val defaultPrimitives = PrimitiveType.defaultPrimitives
val numericPrimitives = PrimitiveType.numericPrimitives
var toNullableT: Boolean = false
var receiverAsterisk = false
val buildFamilies = LinkedHashSet(defaultFamilies)
val buildPrimitives = LinkedHashSet(defaultPrimitives)
val buildFamilyPrimitives = FamilyProperty<Set<PrimitiveType>>()
val customSignature = FamilyProperty<String>()
val deprecate = DeprecationProperty()
val doc = FamilyProperty<String>()
val platformName = PrimitiveProperty<String>()
val inline = FamilyProperty<Boolean>()
val jvmOnly = FamilyProperty<Boolean>()
val typeParams = ArrayList<String>()
val returns = FamilyProperty<String>()
val operator = FamilyProperty<Boolean>()
val infix = FamilyProperty<Boolean>()
val body = object : FamilyProperty<String>() {
override fun onKeySet(key: Family) = include(key)
}
val customPrimitiveBodies = HashMap<Pair<Family, PrimitiveType>, String>()
val annotations = FamilyProperty<String>()
val sourceFile = FamilyProperty<SourceFile>()
fun bodyForTypes(family: Family, vararg primitiveTypes: PrimitiveType, b: () -> String) {
include(family)
for (primitive in primitiveTypes) {
customPrimitiveBodies.put(family to primitive, b())
}
}
fun typeParam(t: String) {
typeParams.add(t)
}
fun receiverAsterisk(v: Boolean) {
receiverAsterisk = v
}
fun exclude(vararg families: Family) {
buildFamilies.removeAll(families.toList())
}
fun only(vararg families: Family) {
buildFamilies.clear()
buildFamilies.addAll(families.toList())
}
fun only(vararg primitives: PrimitiveType) {
only(primitives.asList())
}
fun only(primitives: Collection<PrimitiveType>) {
buildPrimitives.clear()
buildPrimitives.addAll(primitives)
}
fun onlyPrimitives(family: Family, vararg primitives: PrimitiveType) {
buildFamilyPrimitives(family) { primitives.toSet() }
}
fun onlyPrimitives(family: Family, primitives: Set<PrimitiveType>) {
buildFamilyPrimitives(family) { primitives }
}
fun include(vararg families: Family) {
buildFamilies.addAll(families.toList())
}
fun exclude(vararg p: PrimitiveType) {
buildPrimitives.removeAll(p.toList())
}
fun include(vararg p: PrimitiveType) {
buildPrimitives.addAll(p.toList())
}
fun instantiate(vararg families: Family = Family.values()): List<ConcreteFunction> {
return families
.sortedBy { it.name }
.filter { buildFamilies.contains(it) }
.flatMap { family -> instantiate(family) }
}
fun instantiate(f: Family): List<ConcreteFunction> {
val onlyPrimitives = buildFamilyPrimitives[f]
if (f.isPrimitiveSpecialization || onlyPrimitives != null) {
return (onlyPrimitives ?: buildPrimitives).sortedBy { it.name }
.map { primitive -> ConcreteFunction( { build(it, f, primitive) }, sourceFileFor(f) ) }
} else {
return listOf(ConcreteFunction( { build(it, f, null) }, sourceFileFor(f) ))
}
}
private fun sourceFileFor(f: Family) = sourceFile[f] ?: getDefaultSourceFile(f)
private fun getDefaultSourceFile(f: Family): SourceFile = when (f) {
Iterables, Collections, Lists -> SourceFile.Collections
Sequences -> SourceFile.Sequences
Sets -> SourceFile.Sets
Ranges, RangesOfPrimitives, ProgressionsOfPrimitives -> SourceFile.Ranges
ArraysOfObjects, InvariantArraysOfObjects, ArraysOfPrimitives -> SourceFile.Arrays
Maps -> SourceFile.Maps
Strings -> SourceFile.Strings
Primitives, Generic -> SourceFile.Misc
}
fun build(vararg families: Family = Family.values()): String {
val builder = StringBuilder()
for (family in families.sortedBy { it.name }) {
if (buildFamilies.contains(family))
build(builder, family)
}
return builder.toString()
}
fun build(builder: StringBuilder, f: Family) {
val onlyPrimitives = buildFamilyPrimitives[f]
if (f.isPrimitiveSpecialization || onlyPrimitives != null) {
for (primitive in (onlyPrimitives ?: buildPrimitives).sortedBy { it.name })
build(builder, f, primitive)
} else {
build(builder, f, null)
}
}
fun build(builder: Appendable, f: Family, primitive: PrimitiveType?) {
val returnType = returns[f] ?: throw RuntimeException("No return type specified for $signature")
fun renderType(expression: String, receiver: String): String {
val t = StringTokenizer(expression, " \t\n,:()<>?.", true)
val answer = StringBuilder()
while (t.hasMoreTokens()) {
val token = t.nextToken()
answer.append(when (token) {
"SELF" -> receiver
"PRIMITIVE" -> primitive?.name ?: token
"SUM" -> {
when (primitive) {
PrimitiveType.Byte, PrimitiveType.Short, PrimitiveType.Char -> "Int"
else -> primitive
}
}
"ZERO" -> when (primitive) {
PrimitiveType.Double -> "0.0"
PrimitiveType.Float -> "0.0f"
PrimitiveType.Long -> "0L"
else -> "0"
}
"ONE" -> when (primitive) {
PrimitiveType.Double -> "1.0"
PrimitiveType.Float -> "1.0f"
PrimitiveType.Long -> "1L"
else -> "1"
}
"-ONE" -> when (primitive) {
PrimitiveType.Double -> "-1.0"
PrimitiveType.Float -> "-1.0f"
PrimitiveType.Long -> "-1L"
else -> "-1"
}
"TCollection" -> {
when (f) {
Strings -> "Appendable"
else -> renderType("MutableCollection<in T>", receiver)
}
}
"T" -> {
when (f) {
Generic -> "T"
Strings -> "Char"
Maps -> "Map.Entry<K, V>"
else -> primitive?.name ?: token
}
}
"TRange" -> {
when (f) {
Generic -> "Range<T>"
else -> primitive!!.name + "Range"
}
}
"TProgression" -> {
when (f) {
Generic -> "Progression<out T>"
else -> primitive!!.name + "Progression"
}
}
else -> token
})
}
return answer.toString()
}
val isAsteriskOrT = if (receiverAsterisk) "*" else "T"
val receiver = when (f) {
Iterables -> "Iterable<$isAsteriskOrT>"
Collections -> "Collection<$isAsteriskOrT>"
Lists -> "List<$isAsteriskOrT>"
Maps -> "Map<K, V>"
Sets -> "Set<$isAsteriskOrT>"
Sequences -> "Sequence<$isAsteriskOrT>"
InvariantArraysOfObjects -> "Array<T>"
ArraysOfObjects -> "Array<${isAsteriskOrT.replace("T", "out T")}>"
Strings -> "String"
Ranges -> "Range<$isAsteriskOrT>"
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"
}.let { renderType(it, it) }
fun String.renderType(): String = renderType(this, receiver)
fun effectiveTypeParams(): List<String> {
fun getGenericTypeParameters(genericType: String) =
genericType
.dropWhile { it != '<' }
.drop(1)
.takeWhile { it != '>' }
.split(",")
.map { it.removePrefix("out").removePrefix("in").trim() }
// TODO: Model for type parameter
val types = ArrayList(typeParams)
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 = getGenericTypeParameters(receiver) + types.flatMap { getGenericTypeParameters(it) }
for (implicit in implicitTypeParameters.reversed()) {
if (implicit != "*" && !types.any { it.startsWith(implicit) || it.startsWith("reified " + implicit) }) {
types.add(0, implicit)
}
}
return types
} else {
// remove T as parameter
// TODO: Substitute primitive or String instead of T in other parameters from effective types not from original typeParams
return typeParams.filter { !it.startsWith("T") }
}
}
doc[f]?.let { methodDoc ->
builder.append("/**\n")
StringReader(methodDoc).forEachLine {
val line = it.trim()
if (!line.isEmpty()) {
builder.append(" * ").append(line).append("\n")
}
}
builder.append(" */\n")
}
deprecate[f]?.let { deprecated ->
val args = listOfNotNull(
"\"${deprecated.message}\"",
deprecated.replaceWith?.let { "ReplaceWith(\"$it\")" },
deprecated.level.let { if (it != DeprecationLevel.WARNING) "level = DeprecationLevel.$it" else null }
)
builder.append("@Deprecated(${args.joinToString(", ")})\n")
}
if (!f.isPrimitiveSpecialization && primitive != null) {
platformName[primitive]
?.replace("<T>", primitive.name)
?.let { platformName -> builder.append("@kotlin.jvm.JvmName(\"${platformName}\")\n")}
}
if (jvmOnly[f] ?: false) {
builder.append("@kotlin.jvm.JvmVersion\n")
}
annotations[f]?.let { builder.append(it).append('\n') }
builder.append("public ")
if (inline[f] == true)
builder.append("inline ")
if (infix[f] == true)
builder.append("infix ")
if (operator[f] == true)
builder.append("operator ")
builder.append("$keyword ")
val types = effectiveTypeParams()
if (!types.isEmpty()) {
builder.append(types.joinToString(separator = ", ", prefix = "<", postfix = "> ").renderType())
}
val receiverType = (if (toNullableT) receiver.replace("T>", "T?>") else receiver).renderType()
builder.append(receiverType)
builder.append(".${(customSignature[f] ?: signature).renderType()}: ${returnType.renderType()}")
if (keyword == "fun") builder.append(" {")
val body = (
primitive?.let { customPrimitiveBodies[f to primitive] } ?:
body[f] ?:
deprecate[f]?.replaceWith?.let { "return $it" } ?:
throw RuntimeException("No body specified for $signature for ${f to primitive}")
).trim('\n')
val indent: Int = body.takeWhile { it == ' ' }.length
builder.append('\n')
body.lineSequence().forEach {
var count = indent
val line = it.dropWhile { count-- > 0 && it == ' ' }.renderType()
if (!line.isEmpty()) {
builder.append(" ").append(line)
builder.append("\n")
}
}
if (keyword == "fun") builder.append("}\n")
builder.append("\n")
}
}
infix fun MutableList<GenericFunction>.add(item: GenericFunction) = add(item)
infix fun MutableList<GenericFunction>.addAll(items: Iterable<GenericFunction>) = this.addAll(iterable = items)
fun f(signature: String, init: GenericFunction.() -> Unit) = GenericFunction(signature).apply(init)
fun pval(signature: String, init: GenericFunction.() -> Unit) = GenericFunction(signature, "val").apply(init)
fun pvar(signature: String, init: GenericFunction.() -> Unit) = GenericFunction(signature, "var").apply(init)