Add codegen framework

This commit is contained in:
Him188 2020-06-20 22:54:27 +08:00
parent ce49e27e95
commit 754811ddaa
8 changed files with 296 additions and 93 deletions

View File

@ -0,0 +1,98 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("FunctionName", "INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package net.mamoe.mirai.console.codegen
import org.intellij.lang.annotations.Language
abstract class Replacer(private val name: String) : (String) -> String {
override fun toString(): String {
return name
}
}
fun Codegen.Replacer(block: (String) -> String): Replacer {
return object : Replacer(this@Replacer::class.simpleName ?: "<unnamed>") {
override fun invoke(p1: String): String = block(p1)
}
}
class CodegenScope : MutableList<Replacer> by mutableListOf() {
fun applyTo(fileContent: String): String {
return this.fold(fileContent) { acc, replacer -> replacer(acc) }
}
@CodegenDsl
operator fun Codegen.invoke(vararg ktTypes: KtType) {
if (ktTypes.isEmpty() && this is DefaultInvoke) {
invoke(defaultInvokeArgs)
}
invoke(ktTypes.toList())
}
@CodegenDsl
operator fun Codegen.invoke(ktTypes: Collection<KtType>) {
add(Replacer {
it + buildString {
ktTypes.forEach { applyTo(this, it) }
}
})
}
@RegionCodegenDsl
operator fun RegionCodegen.invoke(vararg ktTypes: KtType) = invoke(ktTypes.toList())
@RegionCodegenDsl
operator fun RegionCodegen.invoke(ktTypes: Collection<KtType>) {
add(Replacer {
it.replace(Regex("""//// region $regionName CODEGEN START ////([\s\S]*?)//// endregion $regionName CODEGEN END ////""")) {
val code = CodegenScope().apply { (this@invoke as Codegen).invoke(*ktTypes.toTypedArray()) }.applyTo("")
"""
|//// region $regionName CODEGEN START ////
|
|$code
|
|//// endregion $regionName CODEGEN END ////
""".trimMargin()
}
})
}
@DslMarker
annotation class CodegenDsl
}
@DslMarker
annotation class RegionCodegenDsl
interface DefaultInvoke {
val defaultInvokeArgs: List<KtType>
}
abstract class Codegen {
fun applyTo(stringBuilder: StringBuilder, ktType: KtType) = this.run { stringBuilder.apply(ktType) }
protected abstract fun StringBuilder.apply(ktType: KtType)
}
abstract class RegionCodegen(regionName: String? = null) : Codegen() {
val regionName: String by lazy {
regionName ?: this::class.simpleName!!.substringBefore("Codegen")
}
}
abstract class PrimitiveCodegen : Codegen() {
protected abstract fun StringBuilder.apply(ktType: KtPrimitive)
fun StringBuilder.apply(ktType: List<KtPrimitive>) = ktType.forEach { apply(it) }
}
fun StringBuilder.appendKCode(@Language("kt") ktCode: String): StringBuilder = append(kCode(ktCode)).appendLine()

View File

@ -46,35 +46,37 @@ fun JClazz.getTemplate():String = """
fun main(){ fun main(){
println(buildString { println(buildString {
appendln(COPYRIGHT) appendLine(COPYRIGHT)
appendln() appendLine()
appendln(FILE_SUPPRESS) appendLine(FILE_SUPPRESS)
appendln() appendLine()
appendln("/**\n" + appendLine(
" * !!! This file is auto-generated by backend/codegen/src/kotlin/net.mamoe.mirai.console.codegen.JSettingCodegen.kt\n" + "/**\n" +
" * !!! DO NOT MODIFY THIS FILE MANUALLY\n" + " * !!! This file is auto-generated by backend/codegen/src/kotlin/net.mamoe.mirai.console.codegen.JSettingCodegen.kt\n" +
" */\n" + " * !!! DO NOT MODIFY THIS FILE MANUALLY\n" +
"\"\"\"") " */\n" +
appendln() "\"\"\""
appendln() )
appendLine()
appendLine()
//do simplest //do simplest
(J_EXTRA + J_NUMBERS).forEach { (J_EXTRA + J_NUMBERS).forEach {
appendln(it.getTemplate()) appendLine(it.getTemplate())
} }
(J_EXTRA + J_NUMBERS).forEach { (J_EXTRA + J_NUMBERS).forEach {
appendln(JListClazz(it).getTemplate()) appendLine(JListClazz(it).getTemplate())
} }
(J_EXTRA + J_NUMBERS).forEach { (J_EXTRA + J_NUMBERS).forEach {
appendln(JArrayClazz(it).getTemplate()) appendLine(JArrayClazz(it).getTemplate())
} }
(J_EXTRA + J_NUMBERS).forEach {key -> (J_EXTRA + J_NUMBERS).forEach {key ->
(J_EXTRA + J_NUMBERS).forEach { value -> (J_EXTRA + J_NUMBERS).forEach { value ->
appendln(JMapClazz(key, value).getTemplate()) appendLine(JMapClazz(key, value).getTemplate())
} }
} }
}) })

View File

@ -0,0 +1,40 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.codegen
object SettingCodegen {
object PrimitiveValuesCodegen : RegionCodegen(), DefaultInvoke {
override val defaultInvokeArgs: List<KtType>
get() = KtType.Primitives + KtString
override fun StringBuilder.apply(ktType: KtType) {
@Suppress("ClassName")
appendKCode(
"""
/**
* Represents a non-null [$ktType] value.
*/
interface ${ktType}Value : PrimitiveValue<$ktType>
"""
)
}
}
}
object ValueKtCodegen {
@JvmStatic
fun main(args: Array<String>) {
codegen("Value.kt") {
SettingCodegen.PrimitiveValuesCodegen()
}
}
}

View File

@ -19,19 +19,19 @@ fun main() {
File("backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_Setting.kt").apply { File("backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_Setting.kt").apply {
createNewFile() createNewFile()
}.writeText(buildString { }.writeText(buildString {
appendln(COPYRIGHT) appendLine(COPYRIGHT)
appendln() appendLine()
appendln(FILE_SUPPRESS) appendLine(FILE_SUPPRESS)
appendln() appendLine()
appendln(PACKAGE) appendLine(PACKAGE)
appendln() appendLine()
appendln(IMPORTS) appendLine(IMPORTS)
appendln() appendLine()
appendln() appendLine()
appendln(DO_NOT_MODIFY) appendLine(DO_NOT_MODIFY)
appendln() appendLine()
appendln() appendLine()
appendln(genAllValueUseSite()) appendLine(genAllValueUseSite())
}) })
} }
@ -56,7 +56,7 @@ import kotlin.internal.LowPriorityInOverloadResolution
fun genAllValueUseSite(): String = buildString { fun genAllValueUseSite(): String = buildString {
fun appendln(@Language("kt") code: String) { fun appendln(@Language("kt") code: String) {
this.appendln(code.trimIndent()) this.appendLine(code.trimIndent())
} }
// PRIMITIVE // PRIMITIVE
for (number in NUMBERS + OTHER_PRIMITIVES) { for (number in NUMBERS + OTHER_PRIMITIVES) {
@ -83,7 +83,7 @@ fun genAllValueUseSite(): String = buildString {
// MUTABLE LIST / MUTABLE SET // MUTABLE LIST / MUTABLE SET
for (collectionName in listOf("List", "Set")) { for (collectionName in listOf("List", "Set")) {
for (number in NUMBERS + OTHER_PRIMITIVES) { for (number in NUMBERS + OTHER_PRIMITIVES) {
appendln() appendLine()
appendln( appendln(
""" """
@JvmName("valueMutable") @JvmName("valueMutable")
@ -94,7 +94,7 @@ fun genAllValueUseSite(): String = buildString {
} }
// SPECIAL // SPECIAL
appendln() appendLine()
appendln( appendln(
""" """
fun <T : Setting> Setting.value(default: T): Value<T> { fun <T : Setting> Setting.value(default: T): Value<T> {

View File

@ -19,17 +19,17 @@ fun main() {
File("backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/_ValueImpl.kt").apply { File("backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/internal/_ValueImpl.kt").apply {
createNewFile() createNewFile()
}.writeText(buildString { }.writeText(buildString {
appendln(COPYRIGHT) appendLine(COPYRIGHT)
appendln() appendLine()
appendln(PACKAGE) appendLine(PACKAGE)
appendln() appendLine()
appendln(IMPORTS) appendLine(IMPORTS)
appendln() appendLine()
appendln() appendLine()
appendln(DO_NOT_MODIFY) appendLine(DO_NOT_MODIFY)
appendln() appendLine()
appendln() appendLine()
appendln(genAllValueImpl()) appendLine(genAllValueImpl())
}) })
} }
@ -52,19 +52,19 @@ import net.mamoe.mirai.console.setting.*
fun genAllValueImpl(): String = buildString { fun genAllValueImpl(): String = buildString {
fun appendln(@Language("kt") code: String) { fun appendln(@Language("kt") code: String) {
this.appendln(code.trimIndent()) this.appendLine(code.trimIndent())
} }
// PRIMITIVE // PRIMITIVE
for (number in NUMBERS + OTHER_PRIMITIVES) { for (number in NUMBERS + OTHER_PRIMITIVES) {
appendln(genPrimitiveValueImpl(number, number, "$number.serializer()", false)) appendln(genPrimitiveValueImpl(number, number, "$number.serializer()", false))
appendln() appendLine()
} }
// PRIMITIVE ARRAYS // PRIMITIVE ARRAYS
for (number in NUMBERS + OTHER_PRIMITIVES.filterNot { it == "String" }) { for (number in NUMBERS + OTHER_PRIMITIVES.filterNot { it == "String" }) {
appendln(genPrimitiveValueImpl("${number}Array", "${number}Array", "${number}ArraySerializer()", true)) appendln(genPrimitiveValueImpl("${number}Array", "${number}Array", "${number}ArraySerializer()", true))
appendln() appendLine()
} }
// TYPED ARRAYS // TYPED ARRAYS
@ -77,7 +77,7 @@ fun genAllValueImpl(): String = buildString {
true true
) )
) )
appendln() appendLine()
} }
// PRIMITIVE LISTS / SETS // PRIMITIVE LISTS / SETS
@ -92,11 +92,11 @@ fun genAllValueImpl(): String = buildString {
false false
) )
) )
appendln() appendLine()
} }
} }
appendln() appendLine()
// MUTABLE LIST / MUTABLE SET // MUTABLE LIST / MUTABLE SET
@ -141,11 +141,11 @@ fun genAllValueImpl(): String = buildString {
} }
""" """
) )
appendln() appendLine()
} }
} }
appendln() appendLine()
appendln( appendln(

View File

@ -56,11 +56,11 @@ internal val OTHER_PRIMITIVES = listOf(
fun genPublicApi() = buildString { fun genPublicApi() = buildString {
fun appendln(@Language("kt") code: String) { fun appendln(@Language("kt") code: String) {
this.appendln(code.trimIndent()) this.appendLine(code.trimIndent())
} }
appendln(COPYRIGHT.trim()) appendln(COPYRIGHT.trim())
appendln() appendLine()
appendln( appendln(
""" """
package net.mamoe.mirai.console.setting package net.mamoe.mirai.console.setting
@ -70,7 +70,7 @@ fun genPublicApi() = buildString {
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
""" """
) )
appendln() appendLine()
appendln( appendln(
""" """
/** /**
@ -80,7 +80,7 @@ fun genPublicApi() = buildString {
*/ */
""" """
) )
appendln() appendLine()
appendln( appendln(
""" """
@ -107,7 +107,7 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
} }
""" """
) )
appendln() appendLine()
// PRIMITIVES // PRIMITIVES
@ -127,7 +127,7 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
appendln(template) appendln(template)
} }
appendln() appendLine()
for (number in OTHER_PRIMITIVES) { for (number in OTHER_PRIMITIVES) {
val template = """ val template = """
@ -137,7 +137,7 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
appendln(template) appendln(template)
} }
appendln() appendLine()
// ARRAYS // ARRAYS
@ -154,7 +154,7 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
sealed class PrimitiveArrayValue<T : Any> : ArrayValue<T>() sealed class PrimitiveArrayValue<T : Any> : ArrayValue<T>()
""" """
) )
appendln() appendLine()
for (number in (NUMBERS + OTHER_PRIMITIVES).filterNot { it == "String" }) { for (number in (NUMBERS + OTHER_PRIMITIVES).filterNot { it == "String" }) {
appendln( appendln(
@ -164,10 +164,10 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
} }
""" """
) )
appendln() appendLine()
} }
appendln() appendLine()
// TYPED ARRAYS // TYPED ARRAYS
@ -178,7 +178,7 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
} }
""" """
) )
appendln() appendLine()
for (number in (NUMBERS + OTHER_PRIMITIVES)) { for (number in (NUMBERS + OTHER_PRIMITIVES)) {
appendln( appendln(
@ -188,7 +188,7 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
) )
} }
appendln() appendLine()
// TYPED LISTS / SETS // TYPED LISTS / SETS
for (collectionName in listOf("List", "Set")) { for (collectionName in listOf("List", "Set")) {
@ -207,14 +207,14 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
appendln(template) appendln(template)
} }
appendln() appendLine()
// SETTING // SETTING
appendln( appendln(
""" """
abstract class Setting${collectionName}Value<T: Setting> internal constructor() : Value<${collectionName}<T>>(), ${collectionName}<T> abstract class Setting${collectionName}Value<T: Setting> internal constructor() : Value<${collectionName}<T>>(), ${collectionName}<T>
""" """
) )
appendln() appendLine()
} }
// SETTING VALUE // SETTING VALUE
@ -225,7 +225,7 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
""" """
) )
appendln() appendLine()
// MUTABLE LIST / MUTABLE SET // MUTABLE LIST / MUTABLE SET
for (collectionName in listOf("List", "Set")) { for (collectionName in listOf("List", "Set")) {
@ -235,7 +235,7 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
""" """
) )
appendln() appendLine()
for (number in (NUMBERS + OTHER_PRIMITIVES)) { for (number in (NUMBERS + OTHER_PRIMITIVES)) {
appendln( appendln(
@ -245,17 +245,17 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
) )
} }
appendln() appendLine()
// SETTING // SETTING
appendln( appendln(
""" """
abstract class MutableSetting${collectionName}Value<T: Setting> internal constructor() : Value<Mutable${collectionName}<T>>(), Mutable${collectionName}<T> abstract class MutableSetting${collectionName}Value<T: Setting> internal constructor() : Value<Mutable${collectionName}<T>>(), Mutable${collectionName}<T>
""" """
) )
appendln() appendLine()
} }
appendln() appendLine()
// DYNAMIC // DYNAMIC
appendln( appendln(

View File

@ -1,11 +0,0 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.codegen.setting

View File

@ -7,11 +7,91 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE * https://github.com/mamoe/mirai/blob/master/LICENSE
*/ */
@file:Suppress("NOTHING_TO_INLINE", "MemberVisibilityCanBePrivate", "unused")
package net.mamoe.mirai.console.codegen package net.mamoe.mirai.console.codegen
import org.intellij.lang.annotations.Language
import java.io.File import java.io.File
fun codegen(targetFile: String, regionName: String, block: StringBuilder.() -> Unit) {
typealias KtByte = KtType.KtPrimitive.KtByte
typealias KtShort = KtType.KtPrimitive.KtShort
typealias KtInt = KtType.KtPrimitive.KtInt
typealias KtLong = KtType.KtPrimitive.KtLong
typealias KtFloat = KtType.KtPrimitive.KtFloat
typealias KtDouble = KtType.KtPrimitive.KtDouble
typealias KtChar = KtType.KtPrimitive.KtChar
typealias KtBoolean = KtType.KtPrimitive.KtBoolean
typealias KtString = KtType.KtString
typealias KtCollection = KtType.KtCollection
typealias KtMap = KtType.KtMap
typealias KtPrimitive = KtType.KtPrimitive
sealed class KtType {
/**
* Its classname in standard library
*/
abstract val standardName: String
override fun toString(): String = standardName
/**
* Not Including [String]
*/
sealed class KtPrimitive(
override val standardName: String,
val jPrimitiveName: String = standardName.toLowerCase(),
val jObjectName: String = standardName
) : KtType() {
object KtByte : KtPrimitive("Byte")
object KtShort : KtPrimitive("Short")
object KtInt : KtPrimitive("Int", jObjectName = "Integer")
object KtLong : KtPrimitive("Long")
object KtFloat : KtPrimitive("Float")
object KtDouble : KtPrimitive("Double")
object KtChar : KtPrimitive("Char", jObjectName = "Character")
object KtBoolean : KtPrimitive("Boolean")
}
object KtString : KtType() {
override val standardName: String get() = "String"
}
/**
* [List], [Set]
*/
data class KtCollection(override val standardName: String) : KtType()
object KtMap : KtType() {
override val standardName: String get() = "Map"
}
companion object {
val PrimitiveIntegers = listOf(KtByte, KtShort, KtInt, KtLong)
val PrimitiveFloatings = listOf(KtFloat, KtDouble)
val PrimitiveNumbers = PrimitiveIntegers + PrimitiveFloatings
val PrimitiveNonNumbers = listOf(KtChar, KtBoolean)
val Primitives = PrimitiveNumbers + PrimitiveNonNumbers
}
}
operator fun KtType.plus(type: KtType): List<KtType> {
return listOf(this, type)
}
val KtType.lowerCaseName: String get() = this.standardName.toLowerCase()
inline fun kCode(@Language("kt") source: String) = source.trimIndent()
fun codegen(targetFile: String, block: CodegenScope.() -> Unit) {
//// region PrimitiveValue CODEGEN START //// //// region PrimitiveValue CODEGEN START ////
//// region PrimitiveValue CODEGEN END //// //// region PrimitiveValue CODEGEN END ////
@ -19,17 +99,11 @@ fun codegen(targetFile: String, regionName: String, block: StringBuilder.() -> U
println("Codegen target: ${it.absolutePath}") println("Codegen target: ${it.absolutePath}")
}.apply { }.apply {
writeText( writeText(
readText() CodegenScope().apply(block).also { list ->
.replace(Regex("""//// region $regionName CODEGEN START ////([\s\S]*?)//// endregion $regionName CODEGEN END ////""")) { list.forEach {
val code = StringBuilder().apply(block).toString() println("Applying replacement: $it")
"""
|//// region $regionName CODEGEN START ////
|
|$code
|
|//// endregion $regionName CODEGEN END ////
""".trimMargin()
} }
}.applyTo(readText())
) )
} }
} }
@ -48,7 +122,7 @@ fun String.findFileSmart(): File = kotlin.run {
} }
fun main() { fun main() {
codegen("Value.kt", "PrimitiveValue") { codegen("Value.kt") {
SettingCodegen.PrimitiveValuesCodegen()
} }
} }