Collections standard library is now generated from templates.
This commit is contained in:
@@ -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())"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user