[FIR] Split printer in tree generator to several files

Also use smart printer instead of `PrintWriter`
This commit is contained in:
Dmitriy Novozhilov
2020-01-30 14:37:23 +03:00
parent 075068155b
commit f9a4b8f9cb
15 changed files with 909 additions and 820 deletions
@@ -7,6 +7,8 @@ package org.jetbrains.kotlin.fir.tree.generator
import org.jetbrains.kotlin.fir.tree.generator.context.AbstractFirTreeBuilder
import org.jetbrains.kotlin.fir.tree.generator.model.*
import org.jetbrains.kotlin.fir.tree.generator.printer.GENERATED_MESSAGE
import org.jetbrains.kotlin.fir.tree.generator.printer.printElements
import java.io.File
@@ -1,814 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.tree.generator
import org.jetbrains.kotlin.fir.tree.generator.context.AbstractFirTreeBuilder
import org.jetbrains.kotlin.fir.tree.generator.model.*
import java.io.File
import java.io.PrintWriter
import java.util.*
import kotlin.math.abs
val COPYRIGHT = """
/*
* Copyright 2010-${GregorianCalendar()[Calendar.YEAR]} JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
""".trimIndent()
const val BASE_PACKAGE = "org.jetbrains.kotlin.fir"
//const val BASE_PATH = "compiler/fir/tree/src/"
private const val VISITOR_PACKAGE = "org.jetbrains.kotlin.fir.visitors"
private const val INDENT = " "
val GENERATED_MESSAGE = """
/*
* This file was generated automatically
* DO NOT MODIFY IT MANUALLY
*/
""".trimIndent()
fun printElements(builder: AbstractFirTreeBuilder, generationPath: File) {
builder.elements.forEach { it.generateCode(generationPath) }
builder.elements.flatMap { it.allImplementations }.forEach { it.generateCode(generationPath) }
printVisitor(builder.elements, generationPath)
printVisitorVoid(builder.elements, generationPath)
printTransformer(builder.elements, generationPath)
}
fun PrintWriter.printCopyright() {
println(COPYRIGHT)
println()
}
fun PrintWriter.printGeneratedMessage() {
println(GENERATED_MESSAGE)
println()
}
fun printVisitor(elements: List<Element>, generationPath: File) {
val dir = File(generationPath, VISITOR_PACKAGE.replace(".", "/"))
dir.mkdirs()
File(dir, "FirVisitor.kt").printWriter().use { printer ->
with(printer) {
printCopyright()
println("package $VISITOR_PACKAGE")
println()
elements.forEach { println("import ${it.fullQualifiedName}") }
println()
printGeneratedMessage()
println("abstract class FirVisitor<out R, in D> {")
indent()
println("abstract fun visitElement(element: FirElement, data: D): R")
println()
for (element in elements) {
if (element == AbstractFirTreeBuilder.baseFirElement) continue
with(element) {
indent()
val varName = safeDecapitalizedName
println("open fun ${typeParameters}visit$name($varName: $typeWithArguments, data: D): R${multipleUpperBoundsList()} = visitElement($varName, data)")
println()
}
}
println("}")
}
}
}
fun printVisitorVoid(elements: List<Element>, generationPath: File) {
val dir = File(generationPath, VISITOR_PACKAGE.replace(".", "/"))
dir.mkdirs()
File(dir, "FirVisitorVoid.kt").printWriter().use { printer ->
with(printer) {
printCopyright()
println("package $VISITOR_PACKAGE")
println()
elements.forEach { println("import ${it.fullQualifiedName}") }
println()
printGeneratedMessage()
println("abstract class FirVisitorVoid : FirVisitor<Unit, Nothing?>() {")
indent()
println("abstract fun visitElement(element: FirElement)")
println()
for (element in elements) {
if (element == AbstractFirTreeBuilder.baseFirElement) continue
with(element) {
indent()
val varName = safeDecapitalizedName
println("open fun ${typeParameters}visit$name($varName: $typeWithArguments)${multipleUpperBoundsList()}{")
indent(2)
println("visitElement($varName)")
indent()
println("}")
println()
}
}
for (element in elements) {
with(element) {
indent()
val varName = safeDecapitalizedName
println("final override fun ${typeParameters}visit$name($varName: $typeWithArguments, data: Nothing?)${multipleUpperBoundsList()}{")
indent(2)
println("visit$name($varName)")
indent()
println("}")
println()
}
}
println("}")
}
}
}
fun Element.generateCode(generationPath: File) {
val dir = generationPath.resolve(packageName.replace(".", "/"))
dir.mkdirs()
val file = File(dir, "$type.kt")
file.printWriter().use { printer ->
with(printer) {
printCopyright()
println("package $packageName")
println()
val imports = collectImports()
imports.forEach { println("import $it") }
if (imports.isNotEmpty()) {
println()
}
printGeneratedMessage()
printElement(this@generateCode)
}
}
}
fun Implementation.generateCode(generationPath: File) {
val dir = generationPath.resolve(packageName.replace(".", "/"))
dir.mkdirs()
val file = File(dir, "$type.kt")
file.printWriter().use { printer ->
with(printer) {
printCopyright()
println("package $packageName")
println()
val imports = collectImports()
imports.forEach { println("import $it") }
if (imports.isNotEmpty()) {
println()
}
printGeneratedMessage()
printImplementation(this@generateCode)
}
}
}
val KindOwner.needPureAbstractElement: Boolean get() = (kind != Implementation.Kind.Interface) && !allParents.any { it.kind == Implementation.Kind.AbstractClass }
fun Implementation.collectImports(): List<String> {
return element.collectImportsInternal(
listOf(
element.fullQualifiedName)
+ usedTypes.mapNotNull { it.fullQualifiedName } + parents.mapNotNull { it.fullQualifiedName }
+ listOfNotNull(pureAbstractElementType.fullQualifiedName?.takeIf { needPureAbstractElement }
),
isImpl = true
)
}
fun Element.collectImports(): List<String> {
val baseTypes = parents.mapTo(mutableListOf()) { it.fullQualifiedName }
baseTypes += parentsArguments.values.flatMap { it.values }.mapNotNull { it.fullQualifiedName }
val isBaseFirElement = this == AbstractFirTreeBuilder.baseFirElement
if (isBaseFirElement) {
baseTypes += compositeTransformResultType.fullQualifiedName!!
}
if (needPureAbstractElement) {
baseTypes += pureAbstractElementType.fullQualifiedName!!
}
return collectImportsInternal(
baseTypes,
isImpl = false
)
}
fun Element.collectImportsInternal(base: List<String>, isImpl: Boolean): List<String> {
val fqns = base + allFields.mapNotNull { it.fullQualifiedName } +
allFields.flatMap { it.arguments.mapNotNull { it.fullQualifiedName } } +
typeArguments.flatMap { it.upperBounds.mapNotNull { it.fullQualifiedName } }
val realPackageName = if (isImpl) "$packageName.impl." else "$packageName."
return fqns.filter { fqn ->
fqn.dropLastWhile { it != '.' } != realPackageName
}.distinct().sorted() + "$VISITOR_PACKAGE.*"
}
val Field.isVal: Boolean get() = this is FieldList || (this is FieldWithDefault && origin is FieldList) || !isMutable
fun PrintWriter.printFieldWithDefaultInImplementation(field: Field) {
val defaultValue = field.defaultValue
indent()
print("override ")
if (field.isLateinit) {
print("lateinit ")
}
if (field.isVal) {
print("val")
} else {
print("var")
}
print(" ${field.name}: ${field.mutableType}")
if (field.isLateinit) {
println()
return
} else {
print(" ")
}
if (field.withGetter) {
if (field.customSetter != null) {
println()
indent(2)
}
print("get() ")
}
requireNotNull(defaultValue) {
"No default value for $field"
}
println("= $defaultValue")
field.customSetter?.let {
indent(2)
println("set(value) {")
indent(3)
println(it)
indent(2)
println("}")
}
}
fun PrintWriter.printImplementation(implementation: Implementation) {
fun Field.transform() {
when (this) {
is FieldWithDefault -> origin.transform()
is FirField ->
println("$name = ${name}${call()}transformSingle(transformer, data)")
is FieldList -> {
println("${name}.transformInplace(transformer, data)")
}
else -> throw IllegalStateException()
}
}
with(implementation) {
print("${kind!!.title} $type")
print(element.typeParameters)
val fieldsWithoutDefault = allFields.filter { it.defaultValue == null && !it.isLateinit }
val fieldsWithDefault = allFields.filter { it.defaultValue != null || it.isLateinit }
val isInterface = kind == Implementation.Kind.Interface
val isAbstract = kind == Implementation.Kind.AbstractClass
fun abstract() {
if (isAbstract) {
print("abstract ")
}
}
if (!isInterface && !isAbstract && fieldsWithoutDefault.isNotEmpty()) {
println("(")
fieldsWithoutDefault.forEachIndexed { i, field ->
val end = if (i == fieldsWithoutDefault.size - 1) "" else ","
printField(field, isImplementation = true, override = true, end = end, withIndent = true)
}
print(")")
}
print(" : ")
if (!isInterface && !allParents.any { it.kind == Implementation.Kind.AbstractClass }) {
print("${pureAbstractElementType.type}(), ")
}
// print(element.typeWithArguments)
print(allParents.joinToString { "${it.typeWithArguments}${it.kind.braces()}" })
println(" {")
if (isInterface || isAbstract) {
allFields.forEach {
indent()
abstract()
printField(it, isImplementation = true, override = true, end = "", withIndent = false)
}
} else {
fieldsWithDefault.forEach {
printFieldWithDefaultInImplementation(it)
}
if (fieldsWithDefault.isNotEmpty()) {
println()
}
}
element.allFields.filter { it.type.contains("Symbol") && it !is FieldList }
.takeIf { it.isNotEmpty() && !isInterface && !isAbstract && !element.type.contains("Reference")}
?.let { symbolFields ->
indent(1)
println("init {")
for (symbolField in symbolFields) {
indent(2)
println("${symbolField.name}${symbolField.call()}bind(this)")
}
indent(1)
println("}")
println()
}
fun Field.acceptString(): String = "${name}${call()}accept(visitor, data)"
if (!isInterface && !isAbstract) {
indent(1)
print("override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) {")
if (element.allFirFields.isNotEmpty()) {
println()
for (field in allFields.filter { it.isFirType }) {
if (field.withGetter || !field.needAcceptAndTransform) continue
when (field.name) {
"explicitReceiver" -> {
val explicitReceiver = implementation["explicitReceiver"]!!
val dispatchReceiver = implementation["dispatchReceiver"]!!
val extensionReceiver = implementation["extensionReceiver"]!!
println(
"""
| ${explicitReceiver.acceptString()}
| if (dispatchReceiver !== explicitReceiver) {
| ${dispatchReceiver.acceptString()}
| }
| if (extensionReceiver !== explicitReceiver && extensionReceiver !== dispatchReceiver) {
| ${extensionReceiver.acceptString()}
| }
""".trimMargin()
)
}
"dispatchReceiver", "extensionReceiver", "subjectVariable" -> {
}
"companionObject" -> {
}
else -> {
if (type in setOf("FirClassImpl", "FirSealedClassImpl") && field.name == "declarations") {
indent(2)
println("(declarations.firstOrNull { it is FirConstructorImpl } as? FirConstructorImpl)?.typeParameters?.forEach { it.accept(visitor, data) }")
}
if (type == "FirWhenExpressionImpl" && field.name == "subject") {
println(
"""
| if (subjectVariable != null) {
| subjectVariable.accept(visitor, data)
| } else {
| subject?.accept(visitor, data)
| }
""".trimMargin()
)
} else {
indent(2)
when (field.origin) {
is FirField -> {
println(field.acceptString())
}
is FieldList -> {
println("${field.name}.forEach { it.accept(visitor, data) }")
}
else -> throw IllegalStateException()
}
}
}
}
}
indent()
}
println("}")
println()
}
indent()
abstract()
print("override fun <D> transformChildren(transformer: FirTransformer<D>, data: D): $typeWithArguments")
if (!isInterface && !isAbstract) {
println(" {")
for (field in allFields) {
when {
!field.isMutable || !field.isFirType || field.withGetter || !field.needAcceptAndTransform -> {
}
field.name == "explicitReceiver" -> {
val explicitReceiver = implementation["explicitReceiver"]!!
val dispatchReceiver = implementation["dispatchReceiver"]!!
val extensionReceiver = implementation["extensionReceiver"]!!
if (explicitReceiver.isMutable) {
indent(2)
println("explicitReceiver = explicitReceiver${explicitReceiver.call()}transformSingle(transformer, data)")
}
if (dispatchReceiver.isMutable) {
println(
"""
| if (dispatchReceiver !== explicitReceiver) {
| dispatchReceiver = dispatchReceiver.transformSingle(transformer, data)
| }
""".trimMargin()
)
}
if (extensionReceiver.isMutable) {
println(
"""
| if (extensionReceiver !== explicitReceiver && extensionReceiver !== dispatchReceiver) {
| extensionReceiver = extensionReceiver.transformSingle(transformer, data)
| }
""".trimMargin()
)
}
}
field.name in setOf("dispatchReceiver", "extensionReceiver") -> {
}
type in setOf("FirClassImpl", "FirSealedClassImpl") && field.name == "declarations" -> {
indent(2)
println("(declarations.firstOrNull { it is FirConstructorImpl } as? FirConstructorImpl)?.typeParameters?.transformInplace(transformer, data)")
indent(2)
println("declarations.transformInplace(transformer, data)")
}
field.name == "companionObject" -> {
indent(2)
println("companionObject = declarations.asSequence().filterIsInstance<FirRegularClass>().firstOrNull { it.status.isCompanion }")
}
field.needsSeparateTransform -> {
indent(2)
println("transform${field.name.capitalize()}(transformer, data)")
}
!element.needTransformOtherChildren -> {
indent(2)
field.transform()
}
else -> {
}
}
}
if (element.needTransformOtherChildren) {
indent(2)
println("transformOtherChildren(transformer, data)")
}
indent(2)
println("return this")
indent()
println("}")
} else {
println()
}
for (field in allFields) {
if (!field.needsSeparateTransform) continue
println()
indent()
abstract()
print("override ${field.transformFunctionDeclaration(typeWithArguments)}")
if (isInterface || isAbstract) {
println()
continue
}
println(" {")
if (field.isMutable && field.isFirType) {
// TODO: replace with smth normal
if (type == "FirWhenExpressionImpl" && field.name == "subject") {
println("""
| if (subjectVariable != null) {
| subjectVariable = subjectVariable?.transformSingle(transformer, data)
| } else {
| subject = subject?.transformSingle(transformer, data)
| }
""".trimMargin())
} else {
indent(2)
field.transform()
}
}
indent(2)
println("return this")
indent()
println("}")
}
if (element.needTransformOtherChildren) {
println()
indent()
abstract()
print("override fun <D> transformOtherChildren(transformer: FirTransformer<D>, data: D): $typeWithArguments")
if (isInterface || isAbstract) {
println()
} else {
println(" {")
for (field in allFields) {
if (!field.isMutable || !field.isFirType || field.name == "subjectVariable") continue
if (!field.needsSeparateTransform) {
indent(2)
field.transform()
}
}
indent(2)
println("return this")
indent()
println("}")
}
}
for (field in allFields.filter { it.withReplace }) {
println()
indent()
abstract()
print("override ${field.replaceFunctionDeclaration()}")
if (isInterface || isAbstract) {
println()
continue
}
print(" {")
if (!field.isMutable) {
println("}")
continue
}
println()
indent(2)
val newValue = "new${field.name.capitalize()}"
when {
field.withGetter -> {
}
field.origin is FieldList -> {
println("${field.name}.clear()")
indent(2)
println("${field.name}.addAll($newValue)")
}
else -> {
println("${field.name} = $newValue")
}
}
indent()
println("}")
}
println("}")
}
}
fun Field.transformFunctionDeclaration(returnType: String): String {
return transformFunctionDeclaration(name.capitalize(), returnType)
}
fun transformFunctionDeclaration(transformName: String, returnType: String): String {
return "fun <D> transform$transformName(transformer: FirTransformer<D>, data: D): $returnType"
}
fun Field.replaceFunctionDeclaration(): String {
val capName = name.capitalize()
return "fun replace$capName(new$capName: $typeWithArgumentsWithoutNullablity)"
}
fun printTransformer(elements: List<Element>, generationPath: File) {
val dir = File(generationPath, VISITOR_PACKAGE.replace(".", "/"))
dir.mkdirs()
File(dir, "FirTransformer.kt").printWriter().use { printer ->
with(printer) {
printCopyright()
println("package $VISITOR_PACKAGE")
println()
elements.forEach { println("import ${it.fullQualifiedName}") }
println("import ${compositeTransformResultType.fullQualifiedName}")
println()
printGeneratedMessage()
println("abstract class FirTransformer<in D> : FirVisitor<CompositeTransformResult<FirElement>, D>() {")
println()
indent()
println("abstract fun <E : FirElement> transformElement(element: E, data: D): CompositeTransformResult<E>")
println()
for (element in elements) {
if (element == AbstractFirTreeBuilder.baseFirElement) continue
indent()
val varName = element.safeDecapitalizedName
print("open fun ")
element.typeParameters.takeIf { it.isNotBlank() }?.let { print(it) }
println("transform${element.name}($varName: ${element.typeWithArguments}, data: D): CompositeTransformResult<${element.transformerType.typeWithArguments}>${element.multipleUpperBoundsList()}{")
indent(2)
println("return transformElement($varName, data)")
indent()
println("}")
println()
}
for (element in elements) {
indent()
val varName = element.safeDecapitalizedName
print("final override fun ")
element.typeParameters.takeIf { it.isNotBlank() }?.let { print(it) }
println("visit${element.name}($varName: ${element.typeWithArguments}, data: D): CompositeTransformResult<${element.transformerType.typeWithArguments}>${element.multipleUpperBoundsList()}{")
indent(2)
println("return transform${element.name}($varName, data)")
indent()
println("}")
println()
}
println("}")
}
}
}
fun PrintWriter.printField(field: Field, isImplementation: Boolean, override: Boolean, end: String, withIndent: Boolean) {
if (withIndent) {
indent()
}
if (override) {
print("override ")
}
if (!isImplementation || field.isVal) {
print("val")
} else {
print("var")
}
val type = if (isImplementation) field.mutableType else field.typeWithArguments
println(" ${field.name}: $type$end")
}
val Field.mutableType: String get() = when (this) {
is FieldList -> if (isMutable) "Mutable$typeWithArguments" else typeWithArguments
is FieldWithDefault -> if (isMutable) origin.mutableType else typeWithArguments
else -> typeWithArguments
}
fun Field.call(): String = if (nullable) "?." else "."
fun Element.multipleUpperBoundsList(): String {
return typeArguments.filterIsInstance<TypeArgumentWithMultipleUpperBounds>().takeIf { it.isNotEmpty() }?.let { arguments ->
val upperBoundsList = arguments.joinToString(", ", postfix = " ") { argument ->
argument.upperBounds.joinToString(", ") { upperBound -> "${argument.name} : ${upperBound.typeWithArguments}" }
}
" where $upperBoundsList"
} ?: " "
}
fun Implementation.Kind?.braces(): String = when (this) {
Implementation.Kind.Interface -> ""
Implementation.Kind.OpenClass, Implementation.Kind.AbstractClass -> "()"
else -> throw IllegalStateException(this.toString())
}
fun PrintWriter.printElement(element: Element) {
with(element) {
val isInterface = kind == Implementation.Kind.Interface
fun abstract() {
indent()
if (!isInterface) {
print("abstract ")
}
}
fun override() {
if (this != AbstractFirTreeBuilder.baseFirElement) {
print("override ")
}
}
print("${kind!!.title} $type")
if (typeArguments.isNotEmpty()) {
print(typeArguments.joinToString(", ", "<", ">") { it.toString() })
}
val needPureAbstractElement = !isInterface && !allParents.any { it.kind == Implementation.Kind.AbstractClass }
if (parents.isNotEmpty() || needPureAbstractElement) {
print(" : ")
if (needPureAbstractElement) {
print("${pureAbstractElementType.type}()")
if (parents.isNotEmpty()) {
print(", ")
}
}
print(parents.joinToString(", ") {
var result = it.type
parentsArguments[it]?.let { arguments ->
result += arguments.values.joinToString(", ", "<", ">") { it.typeWithArguments }
}
result + it.kind.braces()
})
}
print(multipleUpperBoundsList())
println("{")
allFields.forEach {
abstract()
printField(it, isImplementation = false, override = it.fromParent, end = "", withIndent = false)
}
if (allFields.isNotEmpty()) {
println()
}
indent()
override()
println("fun <R, D> accept(visitor: FirVisitor<R, D>, data: D): R = visitor.visit$name(this, data)")
fields.filter { it.withReplace }.forEach {
println()
abstract()
if (it.fromParent) print("override ")
println(it.replaceFunctionDeclaration())
}
for (field in allFields) {
if (!field.needsSeparateTransform) continue
println()
abstract()
if (field.fromParent) {
print("override ")
}
println(field.transformFunctionDeclaration(typeWithArguments))
}
if (needTransformOtherChildren) {
println()
abstract()
if (element.parents.any { it.needTransformOtherChildren }) {
print("override ")
}
println(transformFunctionDeclaration("OtherChildren", typeWithArguments))
}
if (element == AbstractFirTreeBuilder.baseFirElement) {
require(isInterface)
println()
indent()
println("fun accept(visitor: FirVisitorVoid) = accept(visitor, null)")
println()
indent()
println("fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D)")
println()
indent()
println("fun acceptChildren(visitor: FirVisitorVoid) = acceptChildren(visitor, null)")
println()
indent()
println("@Suppress(\"UNCHECKED_CAST\")")
indent()
println("fun <E : FirElement, D> transform(visitor: FirTransformer<D>, data: D): CompositeTransformResult<E> =")
indent(2)
println("accept(visitor, data) as CompositeTransformResult<E>")
println()
indent()
println("fun <D> transformChildren(transformer: FirTransformer<D>, data: D): FirElement")
}
println("}")
}
}
// --------------------------------------- Helpers ---------------------------------------
fun PrintWriter.indent(n: Int = 1) {
print(INDENT.repeat(n))
}
val Element.safeDecapitalizedName: String get() = if (name == "Class") "klass" else name.decapitalize()
val Importable.typeWithArguments: String
get() = when (this) {
is AbstractElement -> type + generics
is Implementation -> type + element.generics
is FirField -> element.typeWithArguments + if (nullable) "?" else ""
is Field -> type + generics + if (nullable) "?" else ""
is Type -> type + generics
is ImplementationWithArg -> type + generics
else -> throw IllegalArgumentException()
}
val Importable.typeWithArgumentsWithoutNullablity: String get() = typeWithArguments.dropLastWhile { it == '?' }
val ImplementationWithArg.generics: String
get() = argument?.let { "<${it.type}>" } ?: ""
val AbstractElement.generics: String
get() = typeArguments.takeIf { it.isNotEmpty() }
?.let { it.joinToString(", ", "<", ">") { it.name } }
?: ""
val Field.generics: String
get() = arguments.takeIf { it.isNotEmpty() }
?.let { it.joinToString(", ", "<", ">") { it.typeWithArguments } }
?: ""
val Element.typeParameters: String
get() = typeArguments.takeIf { it.isNotEmpty() }
?.joinToString(", ", "<", "> ")
?: ""
val Type.generics: String
get() = arguments.takeIf { it.isNotEmpty() }?.joinToString(",", "<", ">") ?: ""
@@ -5,9 +5,9 @@
package org.jetbrains.kotlin.fir.tree.generator.context
import org.jetbrains.kotlin.fir.tree.generator.BASE_PACKAGE
import org.jetbrains.kotlin.fir.tree.generator.model.Element
import org.jetbrains.kotlin.fir.tree.generator.model.Type
import org.jetbrains.kotlin.fir.tree.generator.printer.BASE_PACKAGE
import kotlin.reflect.KClass
abstract class AbstractFirTreeBuilder {
@@ -5,9 +5,9 @@
package org.jetbrains.kotlin.fir.tree.generator.context
import org.jetbrains.kotlin.fir.tree.generator.call
import org.jetbrains.kotlin.fir.tree.generator.model.*
import org.jetbrains.kotlin.fir.tree.generator.noReceiverExpressionType
import org.jetbrains.kotlin.fir.tree.generator.printer.call
abstract class AbstractFirTreeImplementationConfigurator {
private val elementsWithImpl = mutableSetOf<Element>()
@@ -5,8 +5,8 @@
package org.jetbrains.kotlin.fir.tree.generator.model
import org.jetbrains.kotlin.fir.tree.generator.BASE_PACKAGE
import org.jetbrains.kotlin.fir.tree.generator.typeWithArguments
import org.jetbrains.kotlin.fir.tree.generator.printer.BASE_PACKAGE
import org.jetbrains.kotlin.fir.tree.generator.printer.typeWithArguments
interface KindOwner : Importable {
var kind: Implementation.Kind?
@@ -7,7 +7,7 @@ package org.jetbrains.kotlin.fir.tree.generator.model
import org.jetbrains.kotlin.fir.tree.generator.context.AbstractFirTreeBuilder
import org.jetbrains.kotlin.fir.tree.generator.context.type
import org.jetbrains.kotlin.fir.tree.generator.typeWithArguments
import org.jetbrains.kotlin.fir.tree.generator.printer.typeWithArguments
// ----------- Simple field -----------
@@ -5,7 +5,7 @@
package org.jetbrains.kotlin.fir.tree.generator.model
import org.jetbrains.kotlin.fir.tree.generator.typeWithArguments
import org.jetbrains.kotlin.fir.tree.generator.printer.typeWithArguments
sealed class Field : Importable {
abstract val name: String
@@ -0,0 +1,51 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.tree.generator.printer
import java.io.File
import java.lang.Appendable
class SmartPrinter(appendable: Appendable) {
private val printer = org.jetbrains.kotlin.utils.Printer(appendable)
private var notFirstPrint: Boolean = false
fun print(vararg objects: Any) {
if (notFirstPrint) {
printer.printWithNoIndent(*objects)
} else {
printer.print(*objects)
}
notFirstPrint = true
}
fun println(vararg objects: Any) {
if (notFirstPrint) {
printer.printlnWithNoIndent(*objects)
} else {
printer.println(*objects)
}
notFirstPrint = false
}
fun pushIndent() {
printer.pushIndent()
}
fun popIndent() {
printer.popIndent()
}
}
inline fun SmartPrinter.withIndent(block: () -> Unit) {
pushIndent()
block()
popIndent()
}
inline fun File.useSmartPrinter(block: SmartPrinter.() -> Unit) {
writer().use { SmartPrinter(it).block() }
}
@@ -0,0 +1,132 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.tree.generator.printer
import org.jetbrains.kotlin.fir.tree.generator.context.AbstractFirTreeBuilder
import org.jetbrains.kotlin.fir.tree.generator.model.Element
import org.jetbrains.kotlin.fir.tree.generator.model.Implementation
import org.jetbrains.kotlin.fir.tree.generator.pureAbstractElementType
import java.io.File
fun Element.generateCode(generationPath: File) {
val dir = generationPath.resolve(packageName.replace(".", "/"))
dir.mkdirs()
val file = File(dir, "$type.kt")
file.useSmartPrinter {
printCopyright()
println("package $packageName")
println()
val imports = collectImports()
imports.forEach { println("import $it") }
if (imports.isNotEmpty()) {
println()
}
printGeneratedMessage()
printElement(this@generateCode)
}
}
fun SmartPrinter.printElement(element: Element) {
with(element) {
val isInterface = kind == Implementation.Kind.Interface
fun abstract() {
if (!isInterface) {
print("abstract ")
}
}
fun override() {
if (this != AbstractFirTreeBuilder.baseFirElement) {
print("override ")
}
}
print("${kind!!.title} $type")
if (typeArguments.isNotEmpty()) {
print(typeArguments.joinToString(", ", "<", ">") { it.toString() })
}
val needPureAbstractElement = !isInterface && !allParents.any { it.kind == Implementation.Kind.AbstractClass }
if (parents.isNotEmpty() || needPureAbstractElement) {
print(" : ")
if (needPureAbstractElement) {
print("${pureAbstractElementType.type}()")
if (parents.isNotEmpty()) {
print(", ")
}
}
print(
parents.joinToString(", ") {
var result = it.type
parentsArguments[it]?.let { arguments ->
result += arguments.values.joinToString(", ", "<", ">") { it.typeWithArguments }
}
result + it.kind.braces()
},
)
}
print(multipleUpperBoundsList())
println("{")
withIndent {
allFields.forEach {
abstract()
printField(it, isImplementation = false, override = it.fromParent, end = "")
}
if (allFields.isNotEmpty()) {
println()
}
override()
println("fun <R, D> accept(visitor: FirVisitor<R, D>, data: D): R = visitor.visit$name(this, data)")
fields.filter { it.withReplace }.forEach {
println()
abstract()
if (it.fromParent) print("override ")
println(it.replaceFunctionDeclaration())
}
for (field in allFields) {
if (!field.needsSeparateTransform) continue
println()
abstract()
if (field.fromParent) {
print("override ")
}
println(field.transformFunctionDeclaration(typeWithArguments))
}
if (needTransformOtherChildren) {
println()
abstract()
if (element.parents.any { it.needTransformOtherChildren }) {
print("override ")
}
println(transformFunctionDeclaration("OtherChildren", typeWithArguments))
}
if (element == AbstractFirTreeBuilder.baseFirElement) {
require(isInterface)
println()
println("fun accept(visitor: FirVisitorVoid) = accept(visitor, null)")
println()
println("fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D)")
println()
println("fun acceptChildren(visitor: FirVisitorVoid) = acceptChildren(visitor, null)")
println()
println("@Suppress(\"UNCHECKED_CAST\")")
println("fun <E : FirElement, D> transform(visitor: FirTransformer<D>, data: D): CompositeTransformResult<E> =")
withIndent {
println("accept(visitor, data) as CompositeTransformResult<E>")
}
println()
println("fun <D> transformChildren(transformer: FirTransformer<D>, data: D): FirElement")
}
}
println("}")
}
}
@@ -0,0 +1,61 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.tree.generator.printer
import org.jetbrains.kotlin.fir.tree.generator.model.Field
fun SmartPrinter.printField(field: Field, isImplementation: Boolean, override: Boolean, end: String) {
if (override) {
print("override ")
}
if (!isImplementation || field.isVal) {
print("val")
} else {
print("var")
}
val type = if (isImplementation) field.mutableType else field.typeWithArguments
println(" ${field.name}: $type$end")
}
fun SmartPrinter.printFieldWithDefaultInImplementation(field: Field) {
val defaultValue = field.defaultValue
print("override ")
if (field.isLateinit) {
print("lateinit ")
}
if (field.isVal) {
print("val")
} else {
print("var")
}
print(" ${field.name}: ${field.mutableType}")
if (field.isLateinit) {
println()
return
} else {
print(" ")
}
if (field.withGetter) {
if (field.customSetter != null) {
println()
pushIndent()
}
print("get() ")
}
requireNotNull(defaultValue) {
"No default value for $field"
}
println("= $defaultValue")
field.customSetter?.let {
println("set(value) {")
pushIndent()
println(it)
popIndent()
println("}")
popIndent()
}
}
@@ -0,0 +1,329 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.tree.generator.printer
import org.jetbrains.kotlin.fir.tree.generator.model.*
import org.jetbrains.kotlin.fir.tree.generator.pureAbstractElementType
import java.io.File
fun Implementation.generateCode(generationPath: File) {
val dir = generationPath.resolve(packageName.replace(".", "/"))
dir.mkdirs()
val file = File(dir, "$type.kt")
file.useSmartPrinter {
printCopyright()
println("package $packageName")
println()
val imports = collectImports()
imports.forEach { println("import $it") }
if (imports.isNotEmpty()) {
println()
}
printGeneratedMessage()
printImplementation(this@generateCode)
}
}
fun SmartPrinter.printImplementation(implementation: Implementation) {
fun Field.transform() {
when (this) {
is FieldWithDefault -> origin.transform()
is FirField ->
println("$name = ${name}${call()}transformSingle(transformer, data)")
is FieldList -> {
println("${name}.transformInplace(transformer, data)")
}
else -> throw IllegalStateException()
}
}
with(implementation) {
print("${kind!!.title} $type")
print(element.typeParameters)
val fieldsWithoutDefault = allFields.filter { it.defaultValue == null && !it.isLateinit }
val fieldsWithDefault = allFields.filter { it.defaultValue != null || it.isLateinit }
val isInterface = kind == Implementation.Kind.Interface
val isAbstract = kind == Implementation.Kind.AbstractClass
fun abstract() {
if (isAbstract) {
print("abstract ")
}
}
if (!isInterface && !isAbstract && fieldsWithoutDefault.isNotEmpty()) {
println("(")
withIndent {
fieldsWithoutDefault.forEachIndexed { i, field ->
val end = if (i == fieldsWithoutDefault.size - 1) "" else ","
printField(field, isImplementation = true, override = true, end = end)
}
}
print(")")
}
print(" : ")
if (!isInterface && !allParents.any { it.kind == Implementation.Kind.AbstractClass }) {
print("${pureAbstractElementType.type}(), ")
}
print(allParents.joinToString { "${it.typeWithArguments}${it.kind.braces()}" })
println(" {")
withIndent {
if (isInterface || isAbstract) {
allFields.forEach {
abstract()
printField(it, isImplementation = true, override = true, end = "")
}
} else {
fieldsWithDefault.forEach {
printFieldWithDefaultInImplementation(it)
}
if (fieldsWithDefault.isNotEmpty()) {
println()
}
}
element.allFields.filter { it.type.contains("Symbol") && it !is FieldList }
.takeIf { it.isNotEmpty() && !isInterface && !isAbstract && !element.type.contains("Reference") }
?.let { symbolFields ->
println("init {")
for (symbolField in symbolFields) {
withIndent {
println("${symbolField.name}${symbolField.call()}bind(this)")
}
}
println("}")
println()
}
fun Field.acceptString(): String = "${name}${call()}accept(visitor, data)"
if (!isInterface && !isAbstract) {
print("override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) {")
if (element.allFirFields.isNotEmpty()) {
println()
withIndent {
for (field in allFields.filter { it.isFirType }) {
if (field.withGetter || !field.needAcceptAndTransform) continue
when (field.name) {
"explicitReceiver" -> {
val explicitReceiver = implementation["explicitReceiver"]!!
val dispatchReceiver = implementation["dispatchReceiver"]!!
val extensionReceiver = implementation["extensionReceiver"]!!
println(
"""
|${explicitReceiver.acceptString()}
| if (dispatchReceiver !== explicitReceiver) {
| ${dispatchReceiver.acceptString()}
| }
| if (extensionReceiver !== explicitReceiver && extensionReceiver !== dispatchReceiver) {
| ${extensionReceiver.acceptString()}
| }
""".trimMargin(),
)
}
"dispatchReceiver", "extensionReceiver", "subjectVariable", "companionObject" -> {
}
else -> {
if (type in setOf("FirClassImpl", "FirSealedClassImpl") && field.name == "declarations") {
println("(declarations.firstOrNull { it is FirConstructorImpl } as? FirConstructorImpl)?.typeParameters?.forEach { it.accept(visitor, data) }")
}
if (type == "FirWhenExpressionImpl" && field.name == "subject") {
println(
"""
|if (subjectVariable != null) {
| subjectVariable.accept(visitor, data)
| } else {
| subject?.accept(visitor, data)
| }
""".trimMargin(),
)
} else {
when (field.origin) {
is FirField -> {
println(field.acceptString())
}
is FieldList -> {
println("${field.name}.forEach { it.accept(visitor, data) }")
}
else -> throw IllegalStateException()
}
}
}
}
}
}
}
println("}")
println()
}
abstract()
print("override fun <D> transformChildren(transformer: FirTransformer<D>, data: D): $typeWithArguments")
if (!isInterface && !isAbstract) {
println(" {")
withIndent {
for (field in allFields) {
when {
!field.isMutable || !field.isFirType || field.withGetter || !field.needAcceptAndTransform -> {}
field.name == "explicitReceiver" -> {
val explicitReceiver = implementation["explicitReceiver"]!!
val dispatchReceiver = implementation["dispatchReceiver"]!!
val extensionReceiver = implementation["extensionReceiver"]!!
if (explicitReceiver.isMutable) {
println("explicitReceiver = explicitReceiver${explicitReceiver.call()}transformSingle(transformer, data)")
}
if (dispatchReceiver.isMutable) {
println(
"""
|if (dispatchReceiver !== explicitReceiver) {
| dispatchReceiver = dispatchReceiver.transformSingle(transformer, data)
| }
""".trimMargin(),
)
}
if (extensionReceiver.isMutable) {
println(
"""
|if (extensionReceiver !== explicitReceiver && extensionReceiver !== dispatchReceiver) {
| extensionReceiver = extensionReceiver.transformSingle(transformer, data)
| }
""".trimMargin(),
)
}
}
field.name in setOf("dispatchReceiver", "extensionReceiver") -> {}
type in setOf("FirClassImpl", "FirSealedClassImpl") && field.name == "declarations" -> {
println("(declarations.firstOrNull { it is FirConstructorImpl } as? FirConstructorImpl)?.typeParameters?.transformInplace(transformer, data)")
println("declarations.transformInplace(transformer, data)")
}
field.name == "companionObject" -> {
println("companionObject = declarations.asSequence().filterIsInstance<FirRegularClass>().firstOrNull { it.status.isCompanion }")
}
field.needsSeparateTransform -> {
println("transform${field.name.capitalize()}(transformer, data)")
}
!element.needTransformOtherChildren -> {
field.transform()
}
else -> {}
}
}
if (element.needTransformOtherChildren) {
println("transformOtherChildren(transformer, data)")
}
println("return this")
}
println("}")
} else {
println()
}
for (field in allFields) {
if (!field.needsSeparateTransform) continue
println()
abstract()
print("override ${field.transformFunctionDeclaration(typeWithArguments)}")
if (isInterface || isAbstract) {
println()
continue
}
println(" {")
withIndent {
if (field.isMutable && field.isFirType) {
// TODO: replace with smth normal
if (type == "FirWhenExpressionImpl" && field.name == "subject") {
println(
"""
|if (subjectVariable != null) {
| subjectVariable = subjectVariable?.transformSingle(transformer, data)
| } else {
| subject = subject?.transformSingle(transformer, data)
| }
""".trimMargin(),
)
} else {
field.transform()
}
}
println("return this")
}
println("}")
}
if (element.needTransformOtherChildren) {
println()
abstract()
print("override fun <D> transformOtherChildren(transformer: FirTransformer<D>, data: D): $typeWithArguments")
if (isInterface || isAbstract) {
println()
} else {
println(" {")
withIndent {
for (field in allFields) {
if (!field.isMutable || !field.isFirType || field.name == "subjectVariable") continue
if (!field.needsSeparateTransform) {
field.transform()
}
}
println("return this")
}
println("}")
}
}
for (field in allFields.filter { it.withReplace }) {
println()
abstract()
print("override ${field.replaceFunctionDeclaration()}")
if (isInterface || isAbstract) {
println()
continue
}
print(" {")
if (!field.isMutable) {
println("}")
continue
}
println()
withIndent {
val newValue = "new${field.name.capitalize()}"
when {
field.withGetter -> {}
field.origin is FieldList -> {
println("${field.name}.clear()")
println("${field.name}.addAll($newValue)")
}
else -> {
println("${field.name} = $newValue")
}
}
}
println("}")
}
}
println("}")
}
}
@@ -0,0 +1,46 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.tree.generator.printer
import org.jetbrains.kotlin.fir.tree.generator.context.AbstractFirTreeBuilder
import java.io.File
import java.util.*
private val COPYRIGHT = """
/*
* Copyright 2010-${GregorianCalendar()[Calendar.YEAR]} JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
""".trimIndent()
const val VISITOR_PACKAGE = "org.jetbrains.kotlin.fir.visitors"
const val BASE_PACKAGE = "org.jetbrains.kotlin.fir"
val GENERATED_MESSAGE = """
/*
* This file was generated automatically
* DO NOT MODIFY IT MANUALLY
*/
""".trimIndent()
fun printElements(builder: AbstractFirTreeBuilder, generationPath: File) {
builder.elements.forEach { it.generateCode(generationPath) }
builder.elements.flatMap { it.allImplementations }.forEach { it.generateCode(generationPath) }
printVisitor(builder.elements, generationPath)
printVisitorVoid(builder.elements, generationPath)
printTransformer(builder.elements, generationPath)
}
fun SmartPrinter.printCopyright() {
println(COPYRIGHT)
println()
}
fun SmartPrinter.printGeneratedMessage() {
println(GENERATED_MESSAGE)
println()
}
@@ -0,0 +1,65 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.tree.generator.printer
import org.jetbrains.kotlin.fir.tree.generator.compositeTransformResultType
import org.jetbrains.kotlin.fir.tree.generator.context.AbstractFirTreeBuilder
import org.jetbrains.kotlin.fir.tree.generator.model.Element
import java.io.File
fun printTransformer(elements: List<Element>, generationPath: File) {
val dir = File(generationPath, VISITOR_PACKAGE.replace(".", "/"))
dir.mkdirs()
File(dir, "FirTransformer.kt").useSmartPrinter {
printCopyright()
println("package $VISITOR_PACKAGE")
println()
elements.forEach { println("import ${it.fullQualifiedName}") }
println("import ${compositeTransformResultType.fullQualifiedName}")
println()
printGeneratedMessage()
println("abstract class FirTransformer<in D> : FirVisitor<CompositeTransformResult<FirElement>, D>() {")
println()
withIndent {
println("abstract fun <E : FirElement> transformElement(element: E, data: D): CompositeTransformResult<E>")
println()
for (element in elements) {
if (element == AbstractFirTreeBuilder.baseFirElement) continue
val varName = element.safeDecapitalizedName
print("open fun ")
element.typeParameters.takeIf { it.isNotBlank() }?.let { print(it) }
println(
"transform${element.name}($varName: ${element.typeWithArguments}, data: D): CompositeTransformResult<${element.transformerType
.typeWithArguments}>${element.multipleUpperBoundsList()}{",
)
withIndent {
println("return transformElement($varName, data)")
}
println("}")
println()
}
for (element in elements) {
val varName = element.safeDecapitalizedName
print("final override fun ")
element.typeParameters.takeIf { it.isNotBlank() }?.let { print(it) }
println(
"visit${element.name}($varName: ${element.typeWithArguments}, data: D): CompositeTransformResult<${element.transformerType
.typeWithArguments}>${element.multipleUpperBoundsList()}{",
)
withIndent {
println("return transform${element.name}($varName, data)")
}
println("}")
println()
}
}
println("}")
}
}
@@ -0,0 +1,131 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.tree.generator.printer
import org.jetbrains.kotlin.fir.tree.generator.compositeTransformResultType
import org.jetbrains.kotlin.fir.tree.generator.context.AbstractFirTreeBuilder
import org.jetbrains.kotlin.fir.tree.generator.model.*
import org.jetbrains.kotlin.fir.tree.generator.pureAbstractElementType
fun Implementation.collectImports(): List<String> {
return element.collectImportsInternal(
listOf(
element.fullQualifiedName,
)
+ usedTypes.mapNotNull { it.fullQualifiedName } + parents.mapNotNull { it.fullQualifiedName }
+ listOfNotNull(
pureAbstractElementType.fullQualifiedName?.takeIf { needPureAbstractElement },
),
isImpl = true,
)
}
fun Element.collectImports(): List<String> {
val baseTypes = parents.mapTo(mutableListOf()) { it.fullQualifiedName }
baseTypes += parentsArguments.values.flatMap { it.values }.mapNotNull { it.fullQualifiedName }
val isBaseFirElement = this == AbstractFirTreeBuilder.baseFirElement
if (isBaseFirElement) {
baseTypes += compositeTransformResultType.fullQualifiedName!!
}
if (needPureAbstractElement) {
baseTypes += pureAbstractElementType.fullQualifiedName!!
}
return collectImportsInternal(
baseTypes,
isImpl = false,
)
}
private fun Element.collectImportsInternal(base: List<String>, isImpl: Boolean): List<String> {
val fqns = base + allFields.mapNotNull { it.fullQualifiedName } +
allFields.flatMap { it.arguments.mapNotNull { it.fullQualifiedName } } +
typeArguments.flatMap { it.upperBounds.mapNotNull { it.fullQualifiedName } }
val realPackageName = if (isImpl) "$packageName.impl." else "$packageName."
return fqns.filter { fqn ->
fqn.dropLastWhile { it != '.' } != realPackageName
}.distinct().sorted() + "$VISITOR_PACKAGE.*"
}
val KindOwner.needPureAbstractElement: Boolean
get() = (kind != Implementation.Kind.Interface) && !allParents.any { it.kind == Implementation.Kind.AbstractClass }
val Field.isVal: Boolean get() = this is FieldList || (this is FieldWithDefault && origin is FieldList) || !isMutable
fun Field.transformFunctionDeclaration(returnType: String): String {
return transformFunctionDeclaration(name.capitalize(), returnType)
}
fun transformFunctionDeclaration(transformName: String, returnType: String): String {
return "fun <D> transform$transformName(transformer: FirTransformer<D>, data: D): $returnType"
}
fun Field.replaceFunctionDeclaration(): String {
val capName = name.capitalize()
return "fun replace$capName(new$capName: $typeWithArgumentsWithoutNullablity)"
}
val Field.mutableType: String
get() = when (this) {
is FieldList -> if (isMutable) "Mutable$typeWithArguments" else typeWithArguments
is FieldWithDefault -> if (isMutable) origin.mutableType else typeWithArguments
else -> typeWithArguments
}
fun Field.call(): String = if (nullable) "?." else "."
fun Element.multipleUpperBoundsList(): String {
return typeArguments.filterIsInstance<TypeArgumentWithMultipleUpperBounds>().takeIf { it.isNotEmpty() }?.let { arguments ->
val upperBoundsList = arguments.joinToString(", ", postfix = " ") { argument ->
argument.upperBounds.joinToString(", ") { upperBound -> "${argument.name} : ${upperBound.typeWithArguments}" }
}
" where $upperBoundsList"
} ?: " "
}
fun Implementation.Kind?.braces(): String = when (this) {
Implementation.Kind.Interface -> ""
Implementation.Kind.OpenClass, Implementation.Kind.AbstractClass -> "()"
else -> throw IllegalStateException(this.toString())
}
val Element.safeDecapitalizedName: String get() = if (name == "Class") "klass" else name.decapitalize()
val Importable.typeWithArguments: String
get() = when (this) {
is AbstractElement -> type + generics
is Implementation -> type + element.generics
is FirField -> element.typeWithArguments + if (nullable) "?" else ""
is Field -> type + generics + if (nullable) "?" else ""
is Type -> type + generics
is ImplementationWithArg -> type + generics
else -> throw IllegalArgumentException()
}
val Importable.typeWithArgumentsWithoutNullablity: String get() = typeWithArguments.dropLastWhile { it == '?' }
val ImplementationWithArg.generics: String
get() = argument?.let { "<${it.type}>" } ?: ""
val AbstractElement.generics: String
get() = typeArguments.takeIf { it.isNotEmpty() }
?.let { it.joinToString(", ", "<", ">") { it.name } }
?: ""
val Field.generics: String
get() = arguments.takeIf { it.isNotEmpty() }
?.let { it.joinToString(", ", "<", ">") { it.typeWithArguments } }
?: ""
val Element.typeParameters: String
get() = typeArguments.takeIf { it.isNotEmpty() }
?.joinToString(", ", "<", "> ")
?: ""
val Type.generics: String
get() = arguments.takeIf { it.isNotEmpty() }?.joinToString(",", "<", ">") ?: ""
@@ -0,0 +1,86 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.tree.generator.printer
import org.jetbrains.kotlin.fir.tree.generator.context.AbstractFirTreeBuilder
import org.jetbrains.kotlin.fir.tree.generator.model.Element
import java.io.File
import java.io.PrintWriter
fun printVisitor(elements: List<Element>, generationPath: File) {
val dir = File(generationPath, VISITOR_PACKAGE.replace(".", "/"))
dir.mkdirs()
File(dir, "FirVisitor.kt").useSmartPrinter {
printCopyright()
println("package $VISITOR_PACKAGE")
println()
elements.forEach { println("import ${it.fullQualifiedName}") }
println()
printGeneratedMessage()
println("abstract class FirVisitor<out R, in D> {")
pushIndent()
println("abstract fun visitElement(element: FirElement, data: D): R\n")
for (element in elements) {
if (element == AbstractFirTreeBuilder.baseFirElement) continue
with(element) {
val varName = safeDecapitalizedName
println("open fun ${typeParameters}visit$name($varName: $typeWithArguments, data: D): R${multipleUpperBoundsList()} = visitElement($varName, data)")
println()
}
}
popIndent()
println("}")
}
}
fun printVisitorVoid(elements: List<Element>, generationPath: File) {
val dir = File(generationPath, VISITOR_PACKAGE.replace(".", "/"))
dir.mkdirs()
File(dir, "FirVisitorVoid.kt").useSmartPrinter {
printCopyright()
println("package $VISITOR_PACKAGE")
println()
elements.forEach { println("import ${it.fullQualifiedName}") }
println()
printGeneratedMessage()
println("abstract class FirVisitorVoid : FirVisitor<Unit, Nothing?>() {")
withIndent {
println("abstract fun visitElement(element: FirElement)")
println()
for (element in elements) {
if (element == AbstractFirTreeBuilder.baseFirElement) continue
with(element) {
val varName = safeDecapitalizedName
println("open fun ${typeParameters}visit$name($varName: $typeWithArguments)${multipleUpperBoundsList()}{")
withIndent {
println("visitElement($varName)")
}
println("}")
println()
}
}
for (element in elements) {
with(element) {
val varName = safeDecapitalizedName
println("final override fun ${typeParameters}visit$name($varName: $typeWithArguments, data: Nothing?)${multipleUpperBoundsList()}{")
withIndent {
println("visit$name($varName)")
}
println("}")
println()
}
}
}
println("}")
}
}