Fix various inconsistencies in visitor generator output

This commit is contained in:
Simon Ogorodnik
2018-03-15 16:32:19 +03:00
committed by Mikhail Glukhikh
parent 2c626d6c5d
commit f33c16a7db
5 changed files with 246 additions and 224 deletions
@@ -0,0 +1,114 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.visitors.generator
import org.jetbrains.kotlin.utils.Printer
abstract class AbstractVisitorGenerator(val referencesData: DataCollector.ReferencesData) {
fun Printer.generateFunction(
name: String,
parameters: Map<String, String>,
returnType: String,
override: Boolean = false,
final: Boolean = false,
body: (Printer.() -> Unit)?
) {
if (body == null) {
print("abstract ")
} else {
printIndent()
if (!final) {
printWithNoIndent("open ")
}
if (override) {
if (final) {
printWithNoIndent("final ")
}
printWithNoIndent("override ")
}
}
printWithNoIndent("fun ", name, "(")
parameters
.flatMap { (a, b) ->
listOf(a, ": ", b, ", ")
}.dropLast(1)
.forEach {
printWithNoIndent(it)
}
printWithNoIndent(")")
if (returnType != "Unit") {
printWithNoIndent(": ", returnType)
}
if (body != null) {
printlnWithNoIndent(" {")
indented {
body()
}
println("}")
} else {
printlnWithNoIndent()
}
println()
}
protected inline fun Printer.indented(l: () -> Unit) {
pushIndent()
l()
popIndent()
}
protected fun Printer.generateCall(name: String, args: List<String>) {
printWithNoIndent(name, "(")
separatedOneLine(args, ", ")
printWithNoIndent(")")
}
protected fun Printer.separatedOneLine(iterable: Iterable<Any>, separator: Any) {
var first = true
for (element in iterable) {
if (!first) {
printWithNoIndent(separator)
} else {
first = false
}
printWithNoIndent(element)
}
}
protected fun Printer.generateDefaultImports() {
referencesData.usedPackages.forEach {
println("import ", it.asString(), ".*")
}
println()
println()
}
val String.safeName
get() = when (this) {
"class" -> "klass"
else -> this
}
fun generate(): String {
val builder = StringBuilder()
val printer = Printer(builder, " ")
printer.apply {
println(javaClass.getResource("/notice.txt").readText())
println("package $VISITOR_PACKAGE")
println()
generateDefaultImports()
println(WARNING_GENERATED_FILE)
printer.generateContent()
}
return builder.toString()
}
abstract fun Printer.generateContent()
}
@@ -59,6 +59,9 @@ class DataCollector {
return result
}
private fun Map<String, List<String>>.sorted(): Map<String, List<String>> {
return this.toSortedMap().mapValues { (_, v) -> v.sorted() }
}
fun computeResult(): ReferencesData {
val back = references.computeBackReferences()
@@ -70,9 +73,9 @@ class DataCollector {
val cleanBack = back.filterKeys { it in keysToKeep }
return ReferencesData(
cleanBack.computeBackReferences(),
cleanBack,
packagePerClass.filterKeys { it in keysToKeep }.values.distinct()
cleanBack.computeBackReferences().sorted(),
cleanBack.sorted(),
packagePerClass.filterKeys { it in keysToKeep }.values.distinct().sortedBy { it.asString() }
)
}
@@ -0,0 +1,46 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.visitors.generator
import org.jetbrains.kotlin.utils.Printer
class SimpleVisitorGenerator(referencesData: DataCollector.ReferencesData) : AbstractVisitorGenerator(referencesData) {
override fun Printer.generateContent() {
println("abstract class $SIMPLE_VISITOR_NAME<out R, in D> {")
indented {
generateFunction(
"visitElement",
parameters = mapOf(
"element" to FIR_ELEMENT_CLASS_NAME,
"data" to "D"
),
returnType = "R",
body = null
)
referencesData.walkHierarchyTopDown(from = FIR_ELEMENT_CLASS_NAME) { parent, element ->
generateVisit(element, parent)
}
}
println("}")
}
private fun Printer.generateVisit(className: String, parent: String) {
val shortcutName = className.classNameWithoutFir
val parameterName = shortcutName.decapitalize().safeName
generateFunction(
name = "visit$shortcutName",
parameters = mapOf(
parameterName to className,
"data" to "D"
),
returnType = "R"
) {
print("return ")
generateCall("visit${parent.classNameWithoutFir}", listOf(parameterName, "data"))
println()
}
}
}
@@ -0,0 +1,73 @@
/*
* Copyright 2000-2018 JetBrains s.r.o. 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.visitors.generator
import org.jetbrains.kotlin.utils.Printer
class UnitVisitorGenerator(referencesData: DataCollector.ReferencesData) : AbstractVisitorGenerator(referencesData) {
override fun Printer.generateContent() {
println("abstract class $UNIT_VISITOR_NAME : $SIMPLE_VISITOR_NAME<Unit, Nothing?>() {")
indented {
generateFunction(
"visitElement",
parameters = mapOf(
"element" to FIR_ELEMENT_CLASS_NAME
),
returnType = "Unit",
body = null
)
referencesData.walkHierarchyTopDown(FIR_ELEMENT_CLASS_NAME) { parent, klass ->
generateVisit(klass, parent)
}
val trampolines = referencesData.back.let {
it.keys + it.values.flatten()
}.distinct()
trampolines.forEach {
generateTrampolineVisit(it)
}
}
println("}")
}
private fun Printer.generateVisit(className: String, parent: String) {
val shortcutName = className.classNameWithoutFir
val parameterName = shortcutName.decapitalize().safeName
generateFunction(
name = "visit$shortcutName",
parameters = mapOf(
parameterName to className
),
returnType = "Unit"
) {
printIndent()
generateCall("visit${parent.classNameWithoutFir}", listOf(parameterName, "null"))
println()
}
}
private fun Printer.generateTrampolineVisit(className: String) {
val shortcutName = className.classNameWithoutFir
val parameterName = shortcutName.decapitalize().safeName
generateFunction(
name = "visit$shortcutName",
parameters = mapOf(
parameterName to className,
"data" to "Nothing?"
),
returnType = "Unit",
override = true,
final = true
) {
printIndent()
generateCall("visit$shortcutName", listOf(parameterName))
println()
}
}
}
@@ -4,13 +4,13 @@
*/
package org.jetbrains.kotlin.fir.visitors.generator
import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.StandardFileSystems
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.openapi.vfs.local.CoreLocalFileSystem
import com.intellij.psi.PsiManager
import com.intellij.psi.SingleRootFileViewProvider
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.utils.Printer
import java.io.File
@@ -53,20 +53,15 @@ fun main(args: Array<String>) {
val data = dataCollector.computeResult()
packageDirectory
.resolve("${SIMPLE_VISITOR_NAME}Generated.kt")
.writeText(
SimpleVisitorGenerator(data).generate()
)
packageDirectory
.resolve("${UNIT_VISITOR_NAME}Generated.kt")
.writeText(
UnitVisitorGenerator(data).generate()
)
SimpleVisitorGenerator(data).runGenerator(packageDirectory.resolve("${SIMPLE_VISITOR_NAME}Generated.kt"))
UnitVisitorGenerator(data).runGenerator(packageDirectory.resolve("${UNIT_VISITOR_NAME}Generated.kt"))
}
}
fun AbstractVisitorGenerator.runGenerator(file: File) {
file.writeText(StringUtil.convertLineSeparators(this.generate(), System.lineSeparator()))
}
val String.classNameWithoutFir get() = this.removePrefix("Fir")
@@ -79,213 +74,4 @@ fun DataCollector.ReferencesData.walkHierarchyTopDown(from: String, l: (p: Strin
}
}
abstract class AbstractVisitorGenerator(val referencesData: DataCollector.ReferencesData) {
fun Printer.generateFunction(
name: String,
parameters: Map<String, String>,
returnType: String,
override: Boolean = false,
final: Boolean = false,
body: (Printer.() -> Unit)?
) {
if (body == null) {
print("abstract ")
} else {
printIndent()
if (!final) {
printWithNoIndent("open ")
}
if (override) {
if (final) {
printWithNoIndent("final ")
}
printWithNoIndent("override ")
}
}
printWithNoIndent("fun ", name, "(")
parameters
.flatMap { (a, b) ->
listOf(a, ": ", b, ", ")
}.dropLast(1)
.forEach {
printWithNoIndent(it)
}
printWithNoIndent(")")
if (returnType != "Unit") {
printWithNoIndent(": ", returnType)
}
if (body != null) {
printlnWithNoIndent(" {")
indented {
body()
}
println("}")
} else {
printlnWithNoIndent()
}
println()
}
protected inline fun Printer.indented(l: () -> Unit) {
pushIndent()
l()
popIndent()
}
protected fun Printer.generateCall(name: String, args: List<String>) {
printWithNoIndent(name, "(")
separatedOneLine(args, ", ")
printWithNoIndent(")")
}
protected fun Printer.separatedOneLine(iterable: Iterable<Any>, separator: Any) {
var first = true
for (element in iterable) {
if (!first) {
printWithNoIndent(separator)
} else {
first = false
}
printWithNoIndent(element)
}
}
protected fun Printer.generateDefaultImports() {
referencesData.usedPackages.forEach {
println("import ", it.asString(), ".*")
}
println()
println()
}
val String.safeName
get() = when (this) {
"class" -> "klass"
else -> this
}
fun generate(): String {
val builder = StringBuilder()
val printer = Printer(builder, " ")
printer.apply {
println(javaClass.getResource("/notice.txt").readText())
println("package $VISITOR_PACKAGE")
println()
generateDefaultImports()
println(WARNING_GENERATED_FILE)
printer.generateContent()
}
return builder.toString()
}
abstract fun Printer.generateContent()
}
class SimpleVisitorGenerator(referencesData: DataCollector.ReferencesData) : AbstractVisitorGenerator(referencesData) {
override fun Printer.generateContent() {
println("abstract class $SIMPLE_VISITOR_NAME<out R, in D> {")
indented {
generateFunction(
"visitElement",
parameters = mapOf(
"element" to FIR_ELEMENT_CLASS_NAME,
"data" to "D"
),
returnType = "R",
body = null
)
referencesData.walkHierarchyTopDown(from = FIR_ELEMENT_CLASS_NAME) { parent, element ->
generateVisit(element, parent)
}
}
println("}")
}
private fun Printer.generateVisit(className: String, parent: String) {
val shortcutName = className.classNameWithoutFir
val parameterName = shortcutName.decapitalize().safeName
generateFunction(
name = "visit$shortcutName",
parameters = mapOf(
parameterName to className,
"data" to "D"
),
returnType = "R"
) {
print("return ")
generateCall("visit${parent.classNameWithoutFir}", listOf(parameterName, "data"))
println()
}
}
}
class UnitVisitorGenerator(referencesData: DataCollector.ReferencesData) : AbstractVisitorGenerator(referencesData) {
override fun Printer.generateContent() {
println("abstract class $UNIT_VISITOR_NAME : $SIMPLE_VISITOR_NAME<Unit, Nothing?>() {")
indented {
generateFunction(
"visitElement",
parameters = mapOf(
"element" to FIR_ELEMENT_CLASS_NAME
),
returnType = "Unit",
body = null
)
referencesData.walkHierarchyTopDown(FIR_ELEMENT_CLASS_NAME) { parent, klass ->
generateVisit(klass, parent)
}
val trampolines = referencesData.back.let {
it.keys + it.values.flatten()
}.distinct()
trampolines.forEach {
generateTrampolineVisit(it)
}
}
println("}")
}
private fun Printer.generateVisit(className: String, parent: String) {
val shortcutName = className.classNameWithoutFir
val parameterName = shortcutName.decapitalize().safeName
generateFunction(
name = "visit$shortcutName",
parameters = mapOf(
parameterName to className
),
returnType = "Unit"
) {
printIndent()
generateCall("visit${parent.classNameWithoutFir}", listOf(parameterName, "null"))
println()
}
}
private fun Printer.generateTrampolineVisit(className: String) {
val shortcutName = className.classNameWithoutFir
val parameterName = shortcutName.decapitalize().safeName
generateFunction(
name = "visit$shortcutName",
parameters = mapOf(
parameterName to className,
"data" to "Nothing?"
),
returnType = "Unit",
override = true,
final = true
) {
printIndent()
generateCall("visit$shortcutName", listOf(parameterName))
println()
}
}
}