StdLib Generators: parse generic function parameters into primitive model
This commit is contained in:
@@ -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(), "")
|
||||
|
||||
Reference in New Issue
Block a user