Collections standard library is now generated from templates.

This commit is contained in:
Maxim Shafirov
2013-02-12 18:49:29 +04:00
parent 324a39d10b
commit 620aae28d0
78 changed files with 3782 additions and 3890 deletions
@@ -0,0 +1,60 @@
package generators
import java.io.File
import java.io.FileWriter
import java.io.PrintWriter
private fun generateDownTos(outputFile: File, header: String) {
fun getMaxType(fromType: String, toType: String): String {
return when {
fromType == "Double" || toType == "Double" -> "Double"
fromType == "Float" || toType == "Float" -> "Float"
fromType == "Long" || toType == "Long" -> "Long"
fromType == "Int" || toType == "Int" -> "Int"
fromType == "Short" || toType == "Short" -> "Short"
fromType == "Char" || toType == "Char" -> "Char"
else -> "Byte"
}
}
fun generateDownTo(writer: PrintWriter, fromType: String, toType: String) {
val elementType = getMaxType(fromType, toType)
val progressionType = elementType + "Progression"
val fromExpr = if (elementType == fromType) "this" else "this.to$elementType()"
val toExpr = if (elementType == toType) "to" else "to.to$elementType()"
val incrementExpr = when (elementType) {
"Long" -> "-1.toLong()"
"Float" -> "-1.toFloat()"
"Double" -> "-1.0"
else -> "-1"
}
writer.println("""
public inline fun $fromType.downTo(to: $toType): $progressionType {
return $progressionType($fromExpr, $toExpr, $incrementExpr)
}""")
}
println("Writing $outputFile")
outputFile.getParentFile()?.mkdirs()
val writer = PrintWriter(FileWriter(outputFile))
try {
writer.println(header)
writer.println("""
$COMMON_AUTOGENERATED_WARNING
""")
val types = array("Byte", "Char", "Short", "Int", "Long", "Float", "Double")
for (fromType in types) {
for (toType in types) {
generateDownTo(writer, fromType, toType)
}
}
} finally {
writer.close()
}
}
@@ -0,0 +1,189 @@
package generators
import java.io.File
import java.io.FileWriter
import java.io.PrintWriter
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import java.util.ArrayList
import java.util.TreeMap
import org.w3c.dom.*
import org.w3c.dom.events.*
/**
* This tool generates JavaScript stubs for classes available in the JDK which are already available in the browser environment
* such as the W3C DOM
*/
fun generateDomAPI(file: File): Unit {
val packageName = "org.w3c.dom"
val imports = ""
val classes: List<Class<*>> = arrayList(javaClass<Attr>(), javaClass<CDATASection>(),
javaClass<CharacterData>(), javaClass<Comment>(),
javaClass<Document>(), javaClass<DocumentFragment>(), javaClass<DocumentType>(),
javaClass<DOMConfiguration>(),
javaClass<DOMError>(), javaClass<DOMErrorHandler>(),
javaClass<DOMImplementation>(), javaClass<DOMImplementationList>(),
javaClass<DOMLocator>(),
javaClass<DOMStringList>(),
javaClass<Element>(),
javaClass<Entity>(), javaClass<EntityReference>(),
javaClass<NameList>(), javaClass<NamedNodeMap>(), javaClass<Node>(), javaClass<NodeList>(),
javaClass<Notation>(), javaClass<ProcessingInstruction>(),
javaClass<Text>(), javaClass<TypeInfo>(),
javaClass<UserDataHandler>())
writeClassesFile(file, packageName, imports, classes)
}
fun generateDomEventsAPI(file: File): Unit {
val packageName = "org.w3c.dom.events"
val imports = "import org.w3c.dom.*\nimport org.w3c.dom.views.*\n"
val classes: List<Class<*>> = arrayList(javaClass<DocumentEvent>(), javaClass<Event>(),
// TODO see domEventsCode.kt we manually hand craft this for now
// to get the implementation in JS
//
// javaClass<EventListener>(),
javaClass<EventTarget>(),
javaClass<MouseEvent>(), javaClass<MutationEvent>(),
javaClass<UIEvent>())
writeClassesFile(file, packageName, imports, classes)
}
private fun writeClassesFile(file: File, packageName: String, imports: String, classes: List<Class<*>>): Unit {
write(file) {
println("""
package $packageName
$imports
//
// NOTE THIS FILE IS AUTO-GENERATED by the GenerateJavaScriptStubs.kt
// See: https://github.com/JetBrains/kotlin/tree/master/libraries/stdlib
//
import js.noImpl
// Contains stub APIs for the W3C DOM API so we can delegate to the platform DOM instead
""")
fun simpleTypeName(klass: Class<out Any?>?): String {
val answer = klass?.getSimpleName()?.capitalize() ?: "Unit"
return if (answer == "Void") "Unit" else if (answer == "Object") "Any" else answer
}
fun parameterTypeName(klass: Class<out Any?>?): String {
val answer = simpleTypeName(klass)
return if (answer == "String" || answer == "Event" || answer.endsWith("DocumentType")) {
answer + "?"
} else answer
}
for (klass in classes) {
val interfaces = klass.getInterfaces()
val extends = if (interfaces != null && interfaces.size == 1) ": ${interfaces[0]?.getSimpleName()}" else ""
println("native public trait ${klass.getSimpleName()}$extends {")
val methods = klass.getDeclaredMethods()
if (methods != null) {
// lets figure out the properties versus methods
val validMethods = ArrayList<Method>()
val properties = TreeMap<String, PropertyKind>()
for (method in methods) {
if (method != null) {
val name = method.getName() ?: ""
fun propertyName(): String {
val answer = name.substring(3).decapitalize()
return if (answer == "type") {
"`type`"
} else answer
}
fun propertyType() = simpleTypeName(method.getReturnType())
fun propertyKind(method: Method): PropertyKind {
val propName = propertyName()
return properties.getOrPut(propName) { PropertyKind(propName, "val", method) }
}
val params = method.getParameterTypes()!!
val paramSize = params.size
if (name.size > 3) {
if (name.startsWith("get") && paramSize == 0) {
propertyKind(method).typeName = propertyType()
} else if (name.startsWith("set") && paramSize == 1) {
propertyKind(method).kind = "var"
} else {
validMethods.add(method)
}
} else {
validMethods.add(method)
}
}
}
for (pk in properties.values()!!) {
// some properties might not have a getter defined
// so lets ignore those
val typeName = pk.typeName
if (typeName == null) {
validMethods.add(pk.method)
} else {
println(" public ${pk.kind} ${pk.name}: ${typeName}")
}
}
for (method in validMethods) {
val parameterTypes = method.getParameterTypes()!!
// TODO in java 7 its not easy with reflection to get the parameter argument name...
var counter = 0
val parameters = parameterTypes.map{ "arg${++counter}: ${parameterTypeName(it)}" }.makeString(", ")
val returnType = simpleTypeName(method.getReturnType())
println(" public fun ${method.getName()}($parameters): $returnType = js.noImpl")
}
}
val fields = klass.getDeclaredFields()
if (fields != null) {
if (fields.notEmpty()) {
println("")
println(" public class object {")
for (field in fields) {
if (field != null) {
val modifiers = field.getModifiers()
if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)) {
val fieldType = simpleTypeName(field.getType())
try {
val value = field.get(null)
if (value != null) {
val fieldType = simpleTypeName(field.getType())
println(" public val ${field.getName()}: $fieldType = $value")
}
} catch (e: Exception) {
println("Caught: $e")
e.printStackTrace()
}
}
}
}
println(" }")
}
}
println("}")
println("")
}
}
}
class PropertyKind(val name: String, var kind: String, val method: Method, var typeName: String? = null)
fun write(file: File, block: PrintWriter.() -> Unit): Unit {
println("Generating file: ${file.getCanonicalPath()}")
val writer = PrintWriter(FileWriter(file))
writer.use { writer.block() }
}
@@ -0,0 +1,137 @@
package generators
import java.io.*
import templates.*
import templates.Family.*
private 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
//"""
fun generateFile(outFile: File, header: String, inputFile: File, f: (String)-> String) {
generateFile(outFile, header, arrayListOf(inputFile), f)
}
fun generateFile(outFile: File, header: String, inputFile: File, jvmFile: File, f: (String)-> String) {
generateFile(outFile, header, arrayListOf(inputFile, jvmFile), f)
}
fun generateFile(outFile: File, header: String, inputFiles: List<File>, f: (String)-> String) {
outFile.getParentFile()?.mkdirs()
val writer = PrintWriter(FileWriter(outFile))
try {
writer.println(header)
for (file in inputFiles) {
writer.println("""
$COMMON_AUTOGENERATED_WARNING
// Generated from input file: $file
//
""")
println("Parsing $file and writing $outFile")
val reader = FileReader(file).buffered()
try {
// TODO ideally we'd use a filterNot() here :)
val iter = reader.lineIterator()
while (iter.hasNext()) {
val line = iter.next()
if (line.startsWith("package")) continue
val xform = f(line)
writer.println(xform)
}
} finally {
reader.close()
reader.close()
}
}
} finally {
writer.close()
}
}
/**
* Generates methods in the standard library which are mostly identical
* but just using a different input kind.
*
* Kinda like mimicking source macros here, but this avoids the inefficiency of type conversions
* at runtime.
*/
fun main(args: Array<String>) {
require(args.size == 1, "Expecting Kotlin project home path as an argument")
val outDir = File(File(args[0]), "libraries/stdlib/src/generated")
require(outDir.exists(), "${outDir.getPath()} doesn't exist!")
val jsCoreDir = File(args[0], "js/js.libraries/src/core")
require(jsCoreDir.exists(), "${jsCoreDir.getPath()} doesn't exist!")
generateDomAPI(File(jsCoreDir, "dom.kt"))
generateDomEventsAPI(File(jsCoreDir, "domEvents.kt"))
val otherArrayNames = arrayListOf("Boolean", "Byte", "Char", "Short", "Int", "Long", "Float", "Double")
iterators()
templates.writeTo(File(outDir, "Iterators.kt")) {
buildFor(Iterators, "")
}
val iteratorSignatures = templates.map { it.signature.flat() }.toSet()
templates.clear()
collections()
templates.writeTo(File(outDir, "Arrays.kt")) {
buildFor(Arrays, "")
}
for (a in otherArrayNames) {
templates.writeTo(File(outDir, "${a}Arrays.kt")) {
buildFor(PrimitiveArrays, a)
}
}
templates.writeTo(File(outDir, "Iterables.kt")) {
if (iteratorSignatures contains signature.flat()) "" else buildFor(Iterables, "")
}
templates.writeTo(File(outDir, "IteratorsCommon.kt")) {
if (iteratorSignatures contains signature.flat()) "" else buildFor(Iterators, "")
}
templates.writeTo(File(outDir, "Collections.kt")) {
if (iteratorSignatures contains signature.flat()) buildFor(Collections, "") else ""
}
generateDownTos(File(outDir, "DownTo.kt"), "package kotlin")
}
fun String.flat() = this.replaceAll(" ", "")
fun List<GenericFunction>.writeTo(file : File, builder : GenericFunction.() -> String) {
println("Generating file: ${file.getPath()}")
val its = FileWriter(file)
its.use {
its.append("package kotlin\n\n")
its.append("import java.util.*\n\n")
for (t in this) {
its.append(t.builder())
}
}
}
// Pretty hacky way to code generate; ideally we'd be using the AST and just changing the function prototypes
fun replaceGenerics(arrayName: String, it: String): String {
return it.replaceAll(" <in T>", " ").replaceAll("<in T, ", "<").replaceAll("<T, ", "<").replaceAll("<T,", "<").
replaceAll(" <T> ", " ").
replaceAll("<T>", "<${arrayName}>").replaceAll("<in T>", "<${arrayName}>").
replaceAll("\\(T\\)", "(${arrayName})").replaceAll("T\\?", "${arrayName}?").
replaceAll("T,", "${arrayName},").
replaceAll("T\\)", "${arrayName})").
replaceAll(" T ", " ${arrayName} ")
}
@@ -0,0 +1,584 @@
package templates
import templates.Family.*
fun collections() {
f("all(predicate: (T) -> Boolean)") {
doc = "Returns *true* if all elements match the given *predicate*"
returns("Boolean")
body {
"""
for (element in this) if (!predicate(element)) return false
return true
"""
}
}
f("any(predicate: (T) -> Boolean)") {
doc = "Returns *true* if any elements match the given *predicate*"
returns("Boolean")
body {
"""
for (element in this) if (predicate(element)) return true
return false
"""
}
}
f("count(predicate: (T) -> Boolean)") {
doc = "Returns the number of elements which match the given *predicate*"
returns("Int")
body {
"""
var count = 0
for (element in this) if (predicate(element)) count++
return count
"""
}
}
f("find(predicate: (T) -> Boolean)") {
doc = "Returns the first element which matches the given *predicate* or *null* if none matched"
typeParam("T:Any")
returns("T?")
body {
"""
for (element in this) if (predicate(element)) return element
return null
"""
}
}
f("filter(predicate: (T) -> Boolean)") {
doc = "Returns a list containing all elements which match the given *predicate*"
returns("List<T>")
body {
"return filterTo(ArrayList<T>(), predicate)"
}
Iterators.returns("Iterator<T")
Iterators.body {
"return FilterIterator<T>(this, predicate)"
}
}
f("filterTo(result: C, predicate: (T) -> Boolean)") {
doc = "Filters all elements which match the given predicate into the given list"
typeParam("C: MutableCollection<in T>")
returns("C")
body {
"""
for (element in this) if (predicate(element)) result.add(element)
return result
"""
}
}
f("filterNot(predicate: (T) -> Boolean)") {
doc = "Returns a list containing all elements which do not match the given *predicate*"
returns("List<T>")
body {
"return filterNotTo(ArrayList<T>(), predicate)"
}
}
f("filterNotTo(result: C, predicate: (T) -> Boolean)") {
doc = "Returns a list containing all elements which do not match the given *predicate*"
typeParam("C: MutableCollection<in T>")
returns("C")
body {
"""
for (element in this) if (!predicate(element)) result.add(element)
return result
"""
}
}
f("filterNotNull()") {
absentFor(PrimitiveArrays) // Those are inherently non-nulls
doc = "Returns a list containing all the non-*null* elements"
typeParam("T:Any")
toNullableT = true
returns("List<T>")
body {
"return filterNotNullTo<T, ArrayList<T>>(ArrayList<T>())"
}
}
f("filterNotNullTo(result: C)") {
absentFor(PrimitiveArrays) // Those are inherently non-nulls
doc = "Filters all non-*null* elements into the given list"
typeParam("T:Any")
toNullableT = true
typeParam("C: MutableCollection<in T>")
returns("C")
body {
"""
for (element in this) if (element != null) result.add(element)
return result
"""
}
}
f("partition(predicate: (T) -> Boolean)") {
doc = "Partitions this collection into a pair of collections"
returns("Pair<List<T>, List<T>>")
body {
"""
val first = ArrayList<T>()
val second = ArrayList<T>()
for (element in this) {
if (predicate(element)) {
first.add(element)
} else {
second.add(element)
}
}
return Pair(first, second)
"""
}
}
f("map(transform : (T) -> R)") {
doc = "Returns a new List containing the results of applying the given *transform* function to each element in this collection"
typeParam("R")
returns("List<R>")
body {
"return mapTo(ArrayList<R>(), transform)"
}
}
f("mapTo(result: C, transform : (T) -> R)") {
doc = """
Transforms each element of this collection with the given *transform* function and
adds each return value to the given *results* collection
"""
typeParam("R")
typeParam("C: MutableCollection<in R>")
returns("C")
body {
"""
for (item in this)
result.add(transform(item))
return result
"""
}
}
f("flatMap(transform: (T)-> Iterable<R>)") {
doc = "Returns the result of transforming each element to one or more values which are concatenated together into a single list"
typeParam("R")
returns("List<R>")
body {
"return flatMapTo(ArrayList<R>(), transform)"
}
}
f("flatMapTo(result: C, transform: (T) -> Iterable<R>)") {
doc = "Returns the result of transforming each element to one or more values which are concatenated together into a single collection"
typeParam("R")
typeParam("C: MutableCollection<in R>")
returns("C")
body {
"""
for (element in this) {
val list = transform(element)
for (r in list) result.add(r)
}
return result
"""
}
}
f("forEach(operation: (T) -> Unit)") {
doc = "Performs the given *operation* on each element"
returns("Unit")
body {
"""
for (element in this) operation(element)
"""
}
}
f("fold(initial: R, operation: (R, T) -> R)") {
doc = "Folds all elements from from left to right with the *initial* value to perform the operation on sequential pairs of elements"
typeParam("R")
returns("R")
body {
"""
var answer = initial
for (element in this) answer = operation(answer, element)
return answer
"""
}
}
f("foldRight(initial: R, operation: (T, R) -> R)") {
doc = "Folds all elements from right to left with the *initial* value to perform the operation on sequential pairs of elements"
typeParam("R")
returns("R")
absentFor(Iterators, Iterables, Collections)
body {
"""
var r = initial
var index = size - 1
while (index >= 0) {
r = operation(get(index--), r)
}
return r
"""
}
}
f("reduce(operation: (T, T) -> T)") {
doc = """
Applies binary operation to all elements of iterable, going from left to right.
Similar to fold function, but uses the first element as initial value
"""
returns("T")
body {
"""
val iterator = this.iterator()
if (!iterator.hasNext()) {
throw UnsupportedOperationException("Empty iterable can't be reduced")
}
var result: T = iterator.next() //compiler doesn't understand that result will initialized anyway
while (iterator.hasNext()) {
result = operation(result, iterator.next())
}
return result
"""
}
}
f("reduceRight(operation: (T, T) -> T)") {
doc = """
Applies binary operation to all elements of iterable, going from right to left.
Similar to foldRight function, but uses the last element as initial value
"""
returns("T")
absentFor(Iterators, Iterables, Collections)
body {
"""
var index = size - 1
if (index < 0) {
throw UnsupportedOperationException("Empty iterable can't be reduced")
}
var r = get(index--)
while (index >= 0) {
r = operation(get(index--), r)
}
return r
"""
}
}
f("groupBy(toKey: (T) -> K)") {
doc = "Groups the elements in the collection into a new [[Map]] using the supplied *toKey* function to calculate the key to group the elements by"
typeParam("K")
returns("Map<K, List<T>>")
body { "return groupByTo(HashMap<K, MutableList<T>>(), toKey)" }
}
f("groupByTo(result: MutableMap<K, MutableList<T>>, toKey: (T) -> K)") {
typeParam("K")
returns("Map<K, MutableList<T>>")
body {
"""
for (element in this) {
val key = toKey(element)
val list = result.getOrPut(key) { ArrayList<T>() }
list.add(element)
}
return result
"""
}
}
f("drop(n: Int)") {
doc = "Returns a list containing everything but the first *n* elements"
returns("List<T>")
body {
"return dropWhile(countTo(n))"
}
}
f("dropWhile(predicate: (T) -> Boolean)") {
doc = "Returns a list containing the everything but the first elements that satisfy the given *predicate*"
returns("List<T>")
body {
"return dropWhileTo(ArrayList<T>(), predicate)"
}
}
f("dropWhileTo(result: L, predicate: (T) -> Boolean)") {
doc = "Returns a list containing the everything but the first elements that satisfy the given *predicate*"
typeParam("L: MutableList<in T>")
returns("L")
body {
"""
var start = true
for (element in this) {
if (start && predicate(element)) {
// ignore
} else {
start = false
result.add(element)
}
}
return result
"""
}
}
f("take(n: Int)") {
doc = "Returns a list containing the first *n* elements"
returns("List<T>")
body {
"return takeWhile(countTo(n))"
}
}
f("takeWhile(predicate: (T) -> Boolean)") {
doc = "Returns a list containing the first elements that satisfy the given *predicate*"
returns("List<T>")
body {
"return takeWhileTo(ArrayList<T>(), predicate)"
}
}
f("takeWhileTo(result: C, predicate: (T) -> Boolean)") {
doc = "Returns a list containing the first elements that satisfy the given *predicate*"
typeParam("C: MutableCollection<in T>")
returns("C")
body {
"""
for (element in this) if (predicate(element)) result.add(element) else break
return result
"""
}
}
f("toCollection(result: C)") {
doc = "Copies all elements into the given collection"
typeParam("C: MutableCollection<in T>")
returns("C")
body {
"""
for (element in this) result.add(element)
return result
"""
}
}
f("reverse()") {
doc = "Reverses the order the elements into a list"
returns("List<T>")
body {
"""
val list = toCollection(ArrayList<T>())
Collections.reverse(list)
return list
"""
}
}
f("toLinkedList()") {
doc = "Copies all elements into a [[LinkedList]]"
returns("LinkedList<T>")
body { "return toCollection(LinkedList<T>())" }
}
f("toList()") {
doc = "Copies all elements into a [[List]]"
returns("List<T>")
body { "return toCollection(ArrayList<T>())" }
}
f("toSet()") {
doc = "Copies all elements into a [[Set]]"
returns("Set<T>")
body { "return toCollection(LinkedHashSet<T>())" }
}
f("toSortedSet()") {
doc = "Copies all elements into a [[SortedSet]]"
returns("SortedSet<T>")
body { "return toCollection(TreeSet<T>())" }
}
f("requireNoNulls()") {
absentFor(PrimitiveArrays) // Those are inherently non-nulls
doc = "Returns a original Iterable containing all the non-*null* elements, throwing an [[IllegalArgumentException]] if there are any null elements"
typeParam("T:Any")
toNullableT = true
returns("SELF")
body {
val THIS = "\$this"
"""
for (element in this) {
if (element == null) {
throw IllegalArgumentException("null element found in $THIS")
}
}
return this as SELF
"""
}
}
f("plus(element: T)") {
doc = "Creates an [[Iterator]] which iterates over this iterator then the given element at the end"
returns("List<T>")
body {
"""
val answer = ArrayList<T>()
toCollection(answer)
answer.add(element)
return answer
"""
}
}
f("plus(iterator: Iterator<T>)") {
doc = "Creates an [[Iterator]] which iterates over this iterator then the following iterator"
returns("List<T>")
body {
"""
val answer = ArrayList<T>()
toCollection(answer)
for (element in iterator) {
answer.add(element)
}
return answer
"""
}
}
f("plus(collection: Iterable<T>)") {
doc = "Creates an [[Iterator]] which iterates over this iterator then the following collection"
returns("List<T>")
body {
"return plus(collection.iterator())"
}
}
f("withIndices()") {
doc = "Returns an iterator of Pairs(index, data)"
returns("Iterator<Pair<Int, T>>")
body {
"return IndexIterator(iterator())"
}
}
f("sortBy(f: (T) -> R)") {
doc = """
Copies all elements into a [[List]] and sorts it by value of compare_function(element)
E.g. arrayList("two" to 2, "one" to 1).sortBy({it._2}) returns list sorted by second element of tuple
"""
returns("List<T>")
typeParam("R: Comparable<R>")
body {
"""
val sortedList = toCollection(ArrayList<T>())
val sortBy: Comparator<T> = comparator<T> {(x: T, y: T) ->
val xr = f(x)
val yr = f(y)
xr.compareTo(yr)
}
java.util.Collections.sort(sortedList, sortBy)
return sortedList
"""
}
}
f("appendString(buffer: Appendable, separator: String = \", \", prefix: String =\"\", postfix: String = \"\", limit: Int = -1, truncated: String = \"...\")") {
doc =
"""
Appends the string from all the elements separated using the *separator* and using the given *prefix* and *postfix* if supplied
If a collection could be huge you can specify a non-negative value of *limit* which will only show a subset of the collection then it will
a special *truncated* separator (which defaults to "..."
"""
returns("Unit")
body {
"""
buffer.append(prefix)
var count = 0
for (element in this) {
if (++count > 1) buffer.append(separator)
if (limit < 0 || count <= limit) {
val text = if (element == null) "null" else element.toString()
buffer.append(text)
} else break
}
if (limit >= 0 && count > limit) buffer.append(truncated)
buffer.append(postfix)
"""
}
}
f("makeString(separator: String = \", \", prefix: String = \"\", postfix: String = \"\", limit: Int = -1, truncated: String = \"...\")") {
doc = """
Creates a string from all the elements separated using the *separator* and using the given *prefix* and *postfix* if supplied.
If a collection could be huge you can specify a non-negative value of *limit* which will only show a subset of the collection then it will
a special *truncated* separator (which defaults to "..."
"""
returns("String")
body {
"""
val buffer = StringBuilder()
appendString(buffer, separator, prefix, postfix, limit, truncated)
return buffer.toString()
"""
}
}
}
@@ -0,0 +1,165 @@
package templates
import java.util.ArrayList
import templates.Family.*
import java.util.HashSet
import java.util.HashMap
import java.io.StringReader
import java.util.StringTokenizer
enum class Family {
Iterators
Iterables
Collections
Arrays
PrimitiveArrays
}
class GenericFunction(val signature : String) {
var doc : String = ""
var toNullableT : Boolean = false
val isInline : Boolean = true;
val blockedFor = HashSet<Family>()
val bodies = HashMap<Family, String>()
val returnTypes = HashMap<Family, String>()
val typeParams = ArrayList<String>()
fun body(b : () -> String) {
for (f in Family.values()) {
if (bodies[f] == null) f.body(b)
}
}
fun Family.body(b : () -> String) {
bodies[this] = b()
}
fun returns(r : String) {
for (f in Family.values()) {
if (returnTypes[f] == null) f.returns(r)
}
}
fun Family.returns(r:String) {
returnTypes[this] = r
}
fun typeParam(t:String) {
typeParams.add(t)
}
fun absentFor(vararg f : Family) {
blockedFor.addAll(f.toCollection())
}
private fun effectiveTypeParams(f : Family) : List<String> {
val types = ArrayList(typeParams)
if (typeParams.find { it.startsWith("T") } == null) {
types.add(0, "T")
}
if (f == PrimitiveArrays) {
types.remove(types.find { it.startsWith("T") })
}
return types
}
fun buildFor(f: Family, arrName : String = "") : String {
if (blockedFor.contains(f)) return ""
if (returnTypes[f] == null) throw RuntimeException("No return type specified for $signature")
val retType = returnTypes[f]!!
val selftype = when (f) {
Iterables -> "Iterable<T>"
Collections -> "Collection<T>"
Iterators -> "Iterator<T>"
Arrays -> "Array<out T>"
PrimitiveArrays -> "${arrName}Array"
}
fun String.renderType() : String {
val t = StringTokenizer(this, " \t\n,:()<>?.", true)
val answer = StringBuilder()
while (t.hasMoreTokens()) {
val token = t.nextToken()
answer.append(when (token) {
"SELF" -> selftype
"T" -> if (f == Family.PrimitiveArrays) arrName else token
else -> token
})
}
return answer.toString()
}
val builder = StringBuilder()
if (doc != "") {
builder.append("/**\n")
StringReader(doc).forEachLine {
val line = it.trim()
if (!line.isEmpty()) {
builder.append(" * ").append(line).append("\n")
}
}
builder.append(" */\n")
}
builder.append("public ")
if (isInline) builder.append("inline ")
builder.append("fun ")
val types = effectiveTypeParams(f)
if (!types.isEmpty()) {
builder.append(types.makeString(separator = ", ", prefix = "<", postfix = "> ").renderType())
}
builder.append((
if (toNullableT) {
selftype.replace("T>", "T?>")
}
else {
selftype
}).renderType())
builder.append(".${signature.renderType()} : ${retType.renderType()} {")
val body = bodies[f]!!.trim("\n")
val prefix : Int = body.takeWhile { it == ' ' }.length
StringReader(body).forEachLine {
builder.append('\n')
var count = prefix
builder.append(" ").append(it.dropWhile {count-- > 0 && it == ' '} .renderType())
}
return builder.toString().trimTrailingSpaces() + "\n}\n\n"
}
}
fun String.trimTrailingSpaces() : String {
var answer = this;
while (answer.endsWith(' ') || answer.endsWith('\n')) answer = answer.substring(0, answer.length() - 1)
return answer
}
val templates = ArrayList<GenericFunction>()
fun f(signature : String, init : GenericFunction.() -> Unit) {
val gf = GenericFunction(signature)
gf.init()
templates.add(gf)
}
fun main(args : Array<String>) {
collections()
for (t in templates) {
print(t.buildFor(PrimitiveArrays, "Byte"))
}
}
@@ -0,0 +1,119 @@
package templates
fun iterators() {
f("filter(predicate: (T) -> Boolean)") {
doc = "Returns an iterator over elements which match the given *predicate*"
returns("Iterator<T>")
body {
"return FilterIterator<T>(this, predicate)"
}
}
f("filterNot(predicate: (T) -> Boolean)") {
doc = "Returns an iterator over elements which don't match the given *predicate*"
returns("Iterator<T>")
body {
"return filter {!predicate(it)}"
}
}
f("filterNotNull()") {
doc = "Returns an iterator over non-*null* elements"
typeParam("T:Any")
toNullableT = true
returns("Iterator<T>")
body {
"return FilterNotNullIterator(this)"
}
}
f("map(transform : (T) -> R)") {
doc = "Returns an iterator obtained by applying *transform*, a function transforming an object of type *T* into an object of type *R*"
typeParam("R")
returns("Iterator<R>")
body {
"return MapIterator<T, R>(this, transform)"
}
}
f("flatMap(transform: (T) -> Iterator<R>)") {
doc = "Returns an iterator over the concatenated results of transforming each element to one or more values"
typeParam("R")
returns("Iterator<R>")
body {
"return FlatMapIterator<T, R>(this, transform)"
}
}
f("requireNoNulls()") {
doc = "Returns a original Iterable containing all the non-*null* elements, throwing an [[IllegalArgumentException]] if there are any null elements"
typeParam("T:Any")
toNullableT = true
returns("Iterator<T>")
body {
val THIS = "\$this"
"""
return map<T?, T>{
if (it == null) throw IllegalArgumentException("null element in iterator $THIS") else it
}
"""
}
}
f("take(n: Int)") {
doc = "Returns an iterator restricted to the first *n* elements"
returns("Iterator<T>")
body {
"""
var count = n
return takeWhile{ --count >= 0 }
"""
}
}
f("takeWhile(predicate: (T) -> Boolean)") {
doc = "Returns an iterator restricted to the first elements that match the given *predicate*"
returns("Iterator<T>")
body {
"return TakeWhileIterator<T>(this, predicate)"
}
}
// TODO: drop(n), dropWhile
f("plus(element: T)") {
doc = "Creates an [[Iterator]] which iterates over this iterator then the given element at the end"
returns("Iterator<T>")
body {
"return CompositeIterator<T>(this, SingleIterator(element))"
}
}
f("plus(iterator: Iterator<T>)") {
doc = "Creates an [[Iterator]] which iterates over this iterator then the following iterator"
returns("Iterator<T>")
body {
"return CompositeIterator<T>(this, iterator)"
}
}
f("plus(collection: Iterable<T>)") {
doc = "Creates an [[Iterator]] which iterates over this iterator then the following collection"
returns("Iterator<T>")
body {
"return plus(collection.iterator())"
}
}
}