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 return result
} }
private fun Map<String, List<String>>.sorted(): Map<String, List<String>> {
return this.toSortedMap().mapValues { (_, v) -> v.sorted() }
}
fun computeResult(): ReferencesData { fun computeResult(): ReferencesData {
val back = references.computeBackReferences() val back = references.computeBackReferences()
@@ -70,9 +73,9 @@ class DataCollector {
val cleanBack = back.filterKeys { it in keysToKeep } val cleanBack = back.filterKeys { it in keysToKeep }
return ReferencesData( return ReferencesData(
cleanBack.computeBackReferences(), cleanBack.computeBackReferences().sorted(),
cleanBack, cleanBack.sorted(),
packagePerClass.filterKeys { it in keysToKeep }.values.distinct() 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 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.StandardFileSystems
import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.openapi.vfs.local.CoreLocalFileSystem import com.intellij.openapi.vfs.local.CoreLocalFileSystem
import com.intellij.psi.PsiManager import com.intellij.psi.PsiManager
import com.intellij.psi.SingleRootFileViewProvider import com.intellij.psi.SingleRootFileViewProvider
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.utils.Printer
import java.io.File import java.io.File
@@ -53,20 +53,15 @@ fun main(args: Array<String>) {
val data = dataCollector.computeResult() val data = dataCollector.computeResult()
packageDirectory SimpleVisitorGenerator(data).runGenerator(packageDirectory.resolve("${SIMPLE_VISITOR_NAME}Generated.kt"))
.resolve("${SIMPLE_VISITOR_NAME}Generated.kt") UnitVisitorGenerator(data).runGenerator(packageDirectory.resolve("${UNIT_VISITOR_NAME}Generated.kt"))
.writeText(
SimpleVisitorGenerator(data).generate()
)
packageDirectory
.resolve("${UNIT_VISITOR_NAME}Generated.kt")
.writeText(
UnitVisitorGenerator(data).generate()
)
} }
} }
fun AbstractVisitorGenerator.runGenerator(file: File) {
file.writeText(StringUtil.convertLineSeparators(this.generate(), System.lineSeparator()))
}
val String.classNameWithoutFir get() = this.removePrefix("Fir") 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()
}
}
}