Switch production generated stdlib sources to the new DSL and remove old DSL

This commit is contained in:
Ilya Gorbunov
2017-11-10 05:42:02 +03:00
parent db049c0ab6
commit dd0e04edd5
3 changed files with 39 additions and 562 deletions
@@ -2,12 +2,6 @@ package generators
import java.io.*
import templates.*
import templates.Family.*
val COMMON_AUTOGENERATED_WARNING: String = """//
// NOTE THIS FILE IS AUTO-GENERATED by the GenerateStandardLib.kt
// See: https://github.com/JetBrains/kotlin/tree/master/libraries/stdlib
//"""
/**
* Generates methods in the standard library which are mostly identical
@@ -17,105 +11,46 @@ val COMMON_AUTOGENERATED_WARNING: String = """//
* at runtime.
*/
fun main(args: Array<String>) {
val templateGroups = sequenceOf<TemplateGroup>(
// Elements,
// Filtering,
// Ordering,
// ArrayOps,
// Snapshots,
// Mapping,
// SetOps,
// Aggregates,
// Guards,
// Generators,
// StringJoinOps,
// SequenceOps,
// RangeOps,
// Numeric,
// ComparableOps,
// CommonArrays,
// PlatformSpecialized,
// PlatformSpecializedJS,
{ emptySequence() }
)
require(args.size == 1) { "Expecting Kotlin project home path as an argument" }
val baseDir = File(args.first())
val outDir = baseDir.resolve("libraries/stdlib/src/generated")
require(outDir.exists()) { "$outDir doesn't exist!" }
val jsCoreDir = baseDir.resolve("js/js.libraries/src/core/generated")
require(jsCoreDir.exists()) { "$jsCoreDir doesn't exist!" }
generateCollectionsAPI(outDir)
generateCollectionsJsAPI(jsCoreDir)
generateCommonAPI(baseDir.resolve("libraries/stdlib/common/src/generated"))
}
val commonGenerators = sequenceOf(
::elements,
::filtering,
::ordering,
::arrays,
::snapshots,
::mapping,
::sets,
::aggregates,
::guards,
::generators,
::strings,
::sequences,
::ranges,
::numeric,
::comparables,
CommonArrays::templates
)
fun generateCollectionsAPI(outDir: File) {
val templates = (commonGenerators + ::specialJVM).flatMap { it().sortedBy { it.signature }.asSequence() }
templates.groupByFileAndWrite(outDir, Platform.JVM)
}
fun generateCollectionsJsAPI(outDir: File) {
(commonGenerators + ::specialJS).flatMap { it().sortedBy { it.signature }.asSequence() }
.groupByFileAndWrite(outDir, Platform.JS, { "_${it.name.capitalize()}Js.kt"})
}
fun generateCommonAPI(outDir: File) {
(commonGenerators + ::specialJVM).flatMap { it().sortedBy { it.signature }.asSequence() }
.groupByFileAndWrite(outDir, platform = Platform.Common)
}
private fun Sequence<GenericFunction>.groupByFileAndWrite(
outDir: File,
platform: Platform,
fileNameBuilder: (SourceFile) -> String = { "_${it.name.capitalize()}.kt" }
) {
val groupedConcreteFunctions = map { it.instantiate(platform) }.flatten().groupBy { it.sourceFile }
for ((sourceFile, functions) in groupedConcreteFunctions) {
val file = outDir.resolve(fileNameBuilder(sourceFile))
functions.writeTo(file, sourceFile, platform)
fun File.resolveExistingDir(subpath: String) = resolve(subpath).also {
require(it.isDirectory) { "Directory $it doesn't exist"}
}
}
private fun List<ConcreteFunction>.writeTo(file: File, sourceFile: SourceFile, platform: Platform?) {
println("Generating file: $file")
val commonDir = baseDir.resolveExistingDir("libraries/stdlib/common/src/generated")
val jvmDir = baseDir.resolveExistingDir("libraries/stdlib/src/generated")
val jsDir = baseDir.resolveExistingDir("js/js.libraries/src/core/generated")
FileWriter(file).use { writer ->
if (sourceFile.multifile) {
writer.appendln("@file:kotlin.jvm.JvmMultifileClass")
}
writer.appendln("@file:kotlin.jvm.JvmName(\"${sourceFile.jvmClassName}\")")
if (platform == Platform.JVM)
writer.appendln("@file:kotlin.jvm.JvmVersion")
writer.appendln()
writer.append("package ${sourceFile.packageName ?: "kotlin"}\n\n")
writer.append("$COMMON_AUTOGENERATED_WARNING\n\n")
if (platform == Platform.JS) {
writer.appendln("import kotlin.js.*")
if (sourceFile == SourceFile.Arrays) {
writer.appendln("import primitiveArrayConcat")
writer.appendln("import withType")
}
}
writer.appendln("import kotlin.comparisons.*")
if (platform != Platform.Common && sourceFile == SourceFile.Sequences) {
writer.appendln("import kotlin.coroutines.experimental.*")
}
writer.appendln()
for (f in this) {
f.textBuilder(writer)
templateGroups.groupByFileAndWrite { (platform, source) ->
// File("build/out/$platform/$source.kt")
when (platform) {
Platform.Common -> commonDir.resolve("_${source.name.capitalize()}.kt")
Platform.JVM -> jvmDir.resolve("_${source.name.capitalize()}.kt")
Platform.JS -> jsDir.resolve("_${source.name.capitalize()}Js.kt")
Platform.Native -> error("Native is unsupported yet")
}
}
}
@@ -16,10 +16,14 @@
package templates
import generators.COMMON_AUTOGENERATED_WARNING
import java.io.File
import java.io.FileWriter
val COMMON_AUTOGENERATED_WARNING: String = """//
// NOTE THIS FILE IS AUTO-GENERATED by the GenerateStandardLib.kt
// See: https://github.com/JetBrains/kotlin/tree/master/libraries/stdlib
//"""
data class PlatformSourceFile(
val platform: Platform,
val sourceFile: SourceFile
@@ -1,462 +0,0 @@
package templates
import templates.Family.*
import templates.Family.Collections
import java.io.StringReader
import java.util.*
open class BaseSpecializedProperty<TKey: Any, TValue : Any> {
protected open fun onKeySet(key: TKey) {}
var default: TValue? = null
protected set
}
open class PlatformSpecializedProperty<TKey: Any, TValue : Any>() : BaseSpecializedProperty<TKey, TValue>() {
private val valueBuilders = HashMap<Platform?, HashMap<TKey?, ((TKey) -> TValue)>>()
operator fun get(platform: Platform, key: TKey): TValue? = run {
valueBuilders[platform]?.get(key)
?: valueBuilders[null]?.get(key)
?: valueBuilders[platform]?.get(null)
?: valueBuilders[null]?.get(null)
}?.let { it(key) }
fun isSpecializedFor(platform: Platform, key: TKey): Boolean
= valueBuilders[platform]?.contains(key) == true || valueBuilders[null]?.contains(key) == true
operator fun set(platform: Platform?, keys: Collection<TKey>, value: TValue) {
if (platform == null && keys.isEmpty()) default = value
set(platform, keys, { value })
}
operator fun set(platform: Platform?, keys: Collection<TKey>, value: (TKey)->TValue) {
val valueBuilders = valueBuilders.getOrPut(platform) { hashMapOf() }
if (keys.isEmpty())
valueBuilders[null] = value;
else
for (key in keys) {
valueBuilders[key] = value
onKeySet(key)
}
}
}
open class SpecializedProperty<TKey: Any, TValue : Any>() : BaseSpecializedProperty<TKey, TValue>() {
private val valueBuilders = HashMap<TKey?, ((TKey) -> TValue)>()
operator fun get(key: TKey): TValue? = (valueBuilders[key] ?: valueBuilders[null])?.let { it(key) }
operator fun set(keys: Collection<TKey>, value: TValue) {
if (keys.isEmpty()) default = value
set(keys, { value })
}
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 <TKey: Any, TValue : Any> SpecializedProperty<TKey, TValue>.invoke(vararg keys: TKey, valueBuilder: (TKey) -> TValue) = set(keys.asList(), valueBuilder)
operator fun <TKey: Any, TValue : Any> SpecializedProperty<TKey, TValue>.invoke(value: TValue, vararg keys: TKey) = set(keys.asList(), value)
operator fun <TKey: Any, TValue : Any> PlatformSpecializedProperty<TKey, TValue>.invoke(vararg keys: TKey, valueBuilder: (TKey) -> TValue) = set(null, keys.asList(), valueBuilder)
operator fun <TKey: Any, TValue : Any> PlatformSpecializedProperty<TKey, TValue>.invoke(value: TValue, vararg keys: TKey) = set(null, keys.asList(), value)
operator fun <TKey: Any, TValue : Any> PlatformSpecializedProperty<TKey, TValue>.invoke(platform: Platform, vararg keys: TKey, valueBuilder: (TKey) -> TValue) = set(platform, keys.asList(), valueBuilder)
operator fun <TKey: Any, TValue : Any> PlatformSpecializedProperty<TKey, TValue>.invoke(platform: Platform, value: TValue, vararg keys: TKey) = set(platform, keys.asList(), value)
typealias FamilyProperty<TValue> = SpecializedProperty<Family, TValue>
typealias PlatformProperty<TValue> = SpecializedProperty<Platform, TValue>
typealias PlatformFamilyProperty<TValue> = PlatformSpecializedProperty<Family, TValue>
typealias PlatformPrimitiveProperty<TValue> = PlatformSpecializedProperty<PrimitiveType, TValue>
class DeprecationProperty() : PlatformFamilyProperty<Deprecation>()
operator fun DeprecationProperty.invoke(value: String, vararg keys: Family) = set(null, keys.asList(), Deprecation(value))
class DocProperty() : PlatformFamilyProperty<String>()
operator fun DocProperty.invoke(vararg keys: Family, valueBuilder: DocExtensions.(Family) -> String) = set(null, keys.asList(), { f -> valueBuilder(DocExtensions, f) })
class InlineProperty : PlatformFamilyProperty<Inline>()
operator fun InlineProperty.invoke(vararg keys: Family) = set(null, keys.asList(), Inline.Yes)
operator fun InlineProperty.invoke(value: Boolean, vararg keys: Family) = set(null, keys.asList(), if (value) Inline.Yes else Inline.No)
class ConcreteFunction(val textBuilder: (Appendable) -> Unit, val sourceFile: SourceFile)
class GenericFunction(val signature: String, val keyword: String = "fun") {
val defaultFamilies = Family.defaultFamilies
val defaultPrimitives = PrimitiveType.defaultPrimitives
val numericPrimitives = PrimitiveType.numericPrimitives
var toNullableT: Boolean = false
val receiverAsterisk = PlatformFamilyProperty<Boolean>()
val buildFamilies = PlatformProperty<Set<Family>>().apply {
invoke(defaultFamilies)
}
//val buildFamilies = LinkedHashSet(defaultFamilies)
val buildFamilyPrimitives = PlatformFamilyProperty<Set<PrimitiveType>>().apply {
invoke(defaultPrimitives)
}
val customReceiver = PlatformFamilyProperty<String>()
val customSignature = PlatformFamilyProperty<String>()
val deprecate = DeprecationProperty()
val doc = DocProperty()
val platformName = PlatformPrimitiveProperty<String>()
val inline = InlineProperty()
val jvmOnly = FamilyProperty<Boolean>()
val since = PlatformFamilyProperty<String>()
val typeParams = ArrayList<String>()
val returns = PlatformFamilyProperty<String>()
val visibility = FamilyProperty<String>()
val operator = FamilyProperty<Boolean>()
val infix = FamilyProperty<Boolean>()
val external = PlatformFamilyProperty<Boolean>()
val body = object : PlatformFamilyProperty<String>() {
override fun onKeySet(key: Family) = include(key)
}
val customPrimitiveBodies = HashMap<Pair<Family, PrimitiveType>, String>()
val annotations = PlatformFamilyProperty<String>()
val sourceFile = FamilyProperty<SourceFile>()
val sequenceClassification = mutableListOf<SequenceClass>()
fun bodyForTypes(family: Family, vararg primitiveTypes: PrimitiveType, b: (PrimitiveType) -> String) {
include(family)
for (primitive in primitiveTypes) {
customPrimitiveBodies.put(family to primitive, b(primitive))
}
}
fun typeParam(t: String) {
typeParams.add(t)
}
fun sequenceClassification(vararg sequenceClass: SequenceClass) {
sequenceClassification.addAll(sequenceClass)
}
fun exclude(vararg families: Family) {
buildFamilies(buildFamilies.default!! - families)
}
fun only(vararg families: Family) {
buildFamilies(families.toSet())
}
fun only(platform: Platform, vararg families: Family) {
require(families.isNotEmpty()) { "Need to specify families" }
buildFamilies(families.toSet(), platform)
}
fun only(vararg primitives: PrimitiveType) {
only(primitives.asList())
}
fun only(primitives: Collection<PrimitiveType>) {
buildFamilyPrimitives(primitives.toSet())
}
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(buildFamilies.default!! + families)
}
fun exclude(vararg p: PrimitiveType) {
buildFamilyPrimitives(buildFamilyPrimitives.default!! - p)
}
fun instantiate(platform: Platform, vararg families: Family = Family.values()): List<ConcreteFunction> {
return families
.sortedBy { it.ordinal }
.filter { buildFamilies[platform]!!.contains(it) }
.filter { platform == Platform.JVM || jvmOnly[it] != true }
.flatMap { family -> instantiate(family, platform) }
}
fun instantiate(f: Family, platform: Platform): List<ConcreteFunction> {
val onlyPrimitives = buildFamilyPrimitives[platform, f]!!
if (f.isPrimitiveSpecialization || buildFamilyPrimitives.isSpecializedFor(platform, f)) {
return (onlyPrimitives).sortedBy { it.ordinal }
.map { primitive -> ConcreteFunction( { build(it, f, primitive, platform) }, sourceFileFor(f) ) }
} else {
return listOf(ConcreteFunction( { build(it, f, null, platform) }, 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
CharSequences -> 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?, platform: Platform) {
val headerOnly: Boolean = platform == Platform.Common
val hasOptionalParams = (customSignature[platform, f] ?: signature).contains("=")
val returnType = returns[platform, f] ?: throw RuntimeException("No return type specified for $signature")
fun renderType(expression: String, receiver: String, self: String): String {
val t = StringTokenizer(expression, " \t\n,:()<>?.", true)
val answer = StringBuilder()
while (t.hasMoreTokens()) {
val token = t.nextToken()
answer.append(when (token) {
"RECEIVER" -> receiver
"SELF" -> self
"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) {
CharSequences, Strings -> "Appendable"
else -> renderType("MutableCollection<in T>", receiver, self)
}
}
"T" -> {
when (f) {
Generic -> "T"
CharSequences, 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[platform, f] == true) "*" else "T"
val self = (when (f) {
Iterables -> "Iterable<$isAsteriskOrT>"
Collections -> "Collection<$isAsteriskOrT>"
Lists -> "List<$isAsteriskOrT>"
Maps -> "Map<out K, V>"
Sets -> "Set<$isAsteriskOrT>"
Sequences -> "Sequence<$isAsteriskOrT>"
InvariantArraysOfObjects -> "Array<T>"
ArraysOfObjects -> "Array<${isAsteriskOrT.replace("T", "out T")}>"
Strings -> "String"
CharSequences -> "CharSequence"
Ranges -> "ClosedRange<$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"
})
val receiver = (customReceiver[platform, f] ?: self).let { renderType(it, it, self) }
fun String.renderType(): String = renderType(this, receiver, self)
fun effectiveTypeParams(): List<TypeParameter> {
val parameters = typeParams.mapTo(mutableListOf()) { parseTypeParameter(it.renderType()) }
if (f == Generic) {
if (parameters.none { it.name == "T" })
parameters.add(TypeParameter("T"))
return parameters
}
else if (primitive == null && f != Strings && f != CharSequences) {
val mentionedTypes = parseTypeRef(receiver).mentionedTypes() + parameters.flatMap { it.mentionedTypeRefs() }
val implicitTypeParameters = mentionedTypes.filter { it.name.all(Char::isUpperCase) }
for (implicit in implicitTypeParameters.reversed()) {
if (implicit.name != "*" && parameters.none { it.name == implicit.name }) {
parameters.add(0, TypeParameter(implicit.name))
}
}
return parameters
} else {
// substituted T is no longer a parameter
val renderedT = "T".renderType()
return parameters.filterNot { it.name == renderedT }
}
}
doc[platform, f]?.let { methodDoc ->
builder.append("/**\n")
StringReader(methodDoc.trim()).forEachLine { line ->
builder.append(" * ").append(line.trim()).append("\n")
}
if (f == Sequences && sequenceClassification.isNotEmpty()) {
builder.append(" *\n")
builder.append(" * The operation is ${sequenceClassification.joinToString(" and ") { "_${it}_" }}.\n")
}
builder.append(" */\n")
}
deprecate[platform, 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[platform, primitive]
?.replace("<T>", primitive.name)
?.let { platformName -> builder.append("@kotlin.jvm.JvmName(\"${platformName}\")\n")}
}
if (jvmOnly[f] ?: false) {
builder.append("@kotlin.jvm.JvmVersion\n")
}
since[platform, f]?.let { since ->
builder.append("@SinceKotlin(\"$since\")\n")
}
annotations[platform, f]?.let { builder.append(it.trimIndent()).append('\n') }
if (inline[platform, f] == Inline.Only) {
builder.append("@kotlin.internal.InlineOnly").append('\n')
}
builder.append(visibility[f] ?: "public").append(' ')
if (headerOnly && !hasOptionalParams) {
builder.append("expect ")
}
if (external[platform, f] == true)
builder.append("external ")
if (inline[platform, f]?.isInline() == 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 = "> ", transform = { it.original }))
}
val receiverType = (if (toNullableT) receiver.replace("T>", "T?>") else receiver).renderType()
builder.append(receiverType)
if (receiverType.isNotEmpty()) builder.append('.')
builder.append("${(customSignature[platform, f] ?: signature).renderType()}: ${returnType.renderType()}")
if (headerOnly && !hasOptionalParams) {
builder.append("\n\n")
return
}
if (keyword == "fun") builder.append(" {")
val body = (
primitive?.let { customPrimitiveBodies[f to primitive] } ?:
body[platform, f] ?:
deprecate[platform, 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(elements = 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)