StdLib Generators: parse generic function parameters into primitive model

This commit is contained in:
Ilya Gorbunov
2016-01-23 07:00:54 +03:00
parent 210c9ab3db
commit 0eaaee8202
2 changed files with 82 additions and 24 deletions
@@ -92,7 +92,18 @@ class GenericFunction(val signature: String, val keyword: String = "fun") {
class DocProperty() : FamilyProperty<String>()
operator fun DocProperty.invoke(vararg keys: Family, valueBuilder: DocExtensions.(Family) -> String) = set(keys.asList(), { f -> valueBuilder(DocExtensions, f) })
data class TypeParameter(val original: String, val name: String, val constraint: TypeRef? = null) {
constructor(simpleName: String) : this(simpleName, simpleName)
data class TypeRef(val name: String, val typeArguments: List<TypeArgument> = emptyList()) {
fun mentionedTypes(): List<TypeRef> =
if (typeArguments.isEmpty()) listOf(this) else typeArguments.flatMap { it.type.mentionedTypes() }
}
data class TypeArgument(val type: TypeRef)
fun mentionedTypeRefs(): List<TypeRef> = constraint?.mentionedTypes().orEmpty()
}
val defaultFamilies = Family.defaultFamilies
val defaultPrimitives = PrimitiveType.defaultPrimitives
@@ -315,40 +326,28 @@ class GenericFunction(val signature: String, val keyword: String = "fun") {
fun String.renderType(): String = renderType(this, receiver)
fun effectiveTypeParams(): List<String> {
fun removeAnnotations(typeParam: String) =
typeParam.replace("""^(@[\w\.]+\s+)+""".toRegex(), "")
fun effectiveTypeParams(): List<TypeParameter> {
val parameters = typeParams.mapTo(mutableListOf()) { parseTypeParameter(it) }
fun getGenericTypeParameters(genericType: String) =
removeAnnotations(genericType)
.dropWhile { it != '<' }
.drop(1)
.takeWhile { it != '>' }
.split(",")
.map { it.trim().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 { removeAnnotations(it).let { it == "T" || it.startsWith("T:") } }) {
types.add("T")
}
return types
if (parameters.none { it.name == "T" })
parameters.add(TypeParameter("T"))
return parameters
}
else if (primitive == null && f != Strings && f != CharSequences) {
val implicitTypeParameters = getGenericTypeParameters(receiver) + types.flatMap { getGenericTypeParameters(it) }
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 != "*" && !types.any { removeAnnotations(it).let { it.startsWith(implicit) || it.startsWith("reified " + implicit) } }) {
types.add(0, implicit)
if (implicit.name != "*" && parameters.none { it.name == implicit.name }) {
parameters.add(0, TypeParameter(implicit.name))
}
}
return types
return parameters
} 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.filterNot { removeAnnotations(it).startsWith("T") }
return parameters.filterNot { it.name == "T" }
}
}
@@ -396,7 +395,7 @@ class GenericFunction(val signature: String, val keyword: String = "fun") {
val types = effectiveTypeParams()
if (!types.isEmpty()) {
builder.append(types.joinToString(separator = ", ", prefix = "<", postfix = "> ").renderType())
builder.append(types.joinToString(separator = ", ", prefix = "<", postfix = "> ", transform = { it.original }).renderType())
}
val receiverType = (if (toNullableT) receiver.replace("T>", "T?>") else receiver).renderType()
@@ -0,0 +1,59 @@
package templates
import templates.GenericFunction.TypeParameter
import templates.GenericFunction.TypeParameter.*
fun parseTypeParameter(typeString: String): TypeParameter =
removeAnnotations(typeString.trim().removePrefix("reified ")).let { trimmed ->
if (':' in trimmed) {
val (name, constraint) = trimmed.split(':')
TypeParameter(typeString, name.trim(), parseTypeRef(removeAnnotations(constraint.trim())))
} else {
TypeParameter(typeString, trimmed)
}
}
fun parseTypeRef(typeRef: String): TypeRef =
typeRef.trim().run {
if (contains('<') && endsWith('>')) {
val name = substringBefore('<')
val params = substringAfter('<').substringBeforeLast('>')
TypeRef(name, parseArguments(params))
}
else
TypeRef(this)
}
private fun parseTypeArgument(typeParam: String): TypeArgument
= typeParam.trim().removePrefix("in ").removePrefix("out ").let { TypeArgument(parseTypeRef(it)) }
private fun parseArguments(typeParams: String): List<TypeArgument> {
var restParams: String = typeParams
val params = mutableListOf<TypeArgument>()
while (true) {
val comma = restParams.indexOf(',')
if (comma < 0) {
params += parseTypeArgument(restParams)
break
} else {
val open = restParams.indexOf('<')
val close = restParams.indexOf('>')
if (comma !in open..close) {
params += parseTypeArgument(restParams.take(comma))
restParams = restParams.drop(comma + 1)
}
else {
params += parseTypeArgument(restParams.take(close + 1))
val nextComma = restParams.indexOf(',', startIndex = close)
if (nextComma < 0) break
restParams = restParams.drop(nextComma + 1)
}
}
}
return params
}
private fun removeAnnotations(typeParam: String) =
typeParam.replace("""^(@[\w\.]+\s+)+""".toRegex(), "")