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>()
|
class DocProperty() : FamilyProperty<String>()
|
||||||
operator fun DocProperty.invoke(vararg keys: Family, valueBuilder: DocExtensions.(Family) -> String) = set(keys.asList(), { f -> valueBuilder(DocExtensions, f) })
|
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 defaultFamilies = Family.defaultFamilies
|
||||||
val defaultPrimitives = PrimitiveType.defaultPrimitives
|
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 String.renderType(): String = renderType(this, receiver)
|
||||||
|
|
||||||
fun effectiveTypeParams(): List<String> {
|
fun effectiveTypeParams(): List<TypeParameter> {
|
||||||
fun removeAnnotations(typeParam: String) =
|
val parameters = typeParams.mapTo(mutableListOf()) { parseTypeParameter(it) }
|
||||||
typeParam.replace("""^(@[\w\.]+\s+)+""".toRegex(), "")
|
|
||||||
|
|
||||||
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) {
|
if (f == Generic) {
|
||||||
// ensure type parameter T, if it's not added to typeParams before
|
if (parameters.none { it.name == "T" })
|
||||||
if (!types.any { removeAnnotations(it).let { it == "T" || it.startsWith("T:") } }) {
|
parameters.add(TypeParameter("T"))
|
||||||
types.add("T")
|
return parameters
|
||||||
}
|
|
||||||
return types
|
|
||||||
}
|
}
|
||||||
else if (primitive == null && f != Strings && f != CharSequences) {
|
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()) {
|
for (implicit in implicitTypeParameters.reversed()) {
|
||||||
if (implicit != "*" && !types.any { removeAnnotations(it).let { it.startsWith(implicit) || it.startsWith("reified " + implicit) } }) {
|
if (implicit.name != "*" && parameters.none { it.name == implicit.name }) {
|
||||||
types.add(0, implicit)
|
parameters.add(0, TypeParameter(implicit.name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return types
|
return parameters
|
||||||
} else {
|
} else {
|
||||||
// remove T as parameter
|
// remove T as parameter
|
||||||
// TODO: Substitute primitive or String instead of T in other parameters from effective types not from original typeParams
|
// 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()
|
val types = effectiveTypeParams()
|
||||||
if (!types.isEmpty()) {
|
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()
|
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