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(){
println(buildString {
appendln(COPYRIGHT)
appendln()
appendln(FILE_SUPPRESS)
appendln()
appendln("/**\n" +
" * !!! This file is auto-generated by backend/codegen/src/kotlin/net.mamoe.mirai.console.codegen.JSettingCodegen.kt\n" +
" * !!! DO NOT MODIFY THIS FILE MANUALLY\n" +
" */\n" +
"\"\"\"")
appendln()
appendln()
appendLine(COPYRIGHT)
appendLine()
appendLine(FILE_SUPPRESS)
appendLine()
appendLine(
"/**\n" +
" * !!! This file is auto-generated by backend/codegen/src/kotlin/net.mamoe.mirai.console.codegen.JSettingCodegen.kt\n" +
" * !!! DO NOT MODIFY THIS FILE MANUALLY\n" +
" */\n" +
"\"\"\""
)
appendLine()
appendLine()
//do simplest
(J_EXTRA + J_NUMBERS).forEach {
appendln(it.getTemplate())
appendLine(it.getTemplate())
}
(J_EXTRA + J_NUMBERS).forEach {
appendln(JListClazz(it).getTemplate())
appendLine(JListClazz(it).getTemplate())
}
(J_EXTRA + J_NUMBERS).forEach {
appendln(JArrayClazz(it).getTemplate())
appendLine(JArrayClazz(it).getTemplate())
}
(J_EXTRA + J_NUMBERS).forEach {key ->
(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 {
createNewFile()
}.writeText(buildString {
appendln(COPYRIGHT)
appendln()
appendln(FILE_SUPPRESS)
appendln()
appendln(PACKAGE)
appendln()
appendln(IMPORTS)
appendln()
appendln()
appendln(DO_NOT_MODIFY)
appendln()
appendln()
appendln(genAllValueUseSite())
appendLine(COPYRIGHT)
appendLine()
appendLine(FILE_SUPPRESS)
appendLine()
appendLine(PACKAGE)
appendLine()
appendLine(IMPORTS)
appendLine()
appendLine()
appendLine(DO_NOT_MODIFY)
appendLine()
appendLine()
appendLine(genAllValueUseSite())
})
}
@ -56,7 +56,7 @@ import kotlin.internal.LowPriorityInOverloadResolution
fun genAllValueUseSite(): String = buildString {
fun appendln(@Language("kt") code: String) {
this.appendln(code.trimIndent())
this.appendLine(code.trimIndent())
}
// PRIMITIVE
for (number in NUMBERS + OTHER_PRIMITIVES) {
@ -83,7 +83,7 @@ fun genAllValueUseSite(): String = buildString {
// MUTABLE LIST / MUTABLE SET
for (collectionName in listOf("List", "Set")) {
for (number in NUMBERS + OTHER_PRIMITIVES) {
appendln()
appendLine()
appendln(
"""
@JvmName("valueMutable")
@ -94,7 +94,7 @@ fun genAllValueUseSite(): String = buildString {
}
// SPECIAL
appendln()
appendLine()
appendln(
"""
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 {
createNewFile()
}.writeText(buildString {
appendln(COPYRIGHT)
appendln()
appendln(PACKAGE)
appendln()
appendln(IMPORTS)
appendln()
appendln()
appendln(DO_NOT_MODIFY)
appendln()
appendln()
appendln(genAllValueImpl())
appendLine(COPYRIGHT)
appendLine()
appendLine(PACKAGE)
appendLine()
appendLine(IMPORTS)
appendLine()
appendLine()
appendLine(DO_NOT_MODIFY)
appendLine()
appendLine()
appendLine(genAllValueImpl())
})
}
@ -52,19 +52,19 @@ import net.mamoe.mirai.console.setting.*
fun genAllValueImpl(): String = buildString {
fun appendln(@Language("kt") code: String) {
this.appendln(code.trimIndent())
this.appendLine(code.trimIndent())
}
// PRIMITIVE
for (number in NUMBERS + OTHER_PRIMITIVES) {
appendln(genPrimitiveValueImpl(number, number, "$number.serializer()", false))
appendln()
appendLine()
}
// PRIMITIVE ARRAYS
for (number in NUMBERS + OTHER_PRIMITIVES.filterNot { it == "String" }) {
appendln(genPrimitiveValueImpl("${number}Array", "${number}Array", "${number}ArraySerializer()", true))
appendln()
appendLine()
}
// TYPED ARRAYS
@ -77,7 +77,7 @@ fun genAllValueImpl(): String = buildString {
true
)
)
appendln()
appendLine()
}
// PRIMITIVE LISTS / SETS
@ -92,11 +92,11 @@ fun genAllValueImpl(): String = buildString {
false
)
)
appendln()
appendLine()
}
}
appendln()
appendLine()
// MUTABLE LIST / MUTABLE SET
@ -141,11 +141,11 @@ fun genAllValueImpl(): String = buildString {
}
"""
)
appendln()
appendLine()
}
}
appendln()
appendLine()
appendln(

View File

@ -56,11 +56,11 @@ internal val OTHER_PRIMITIVES = listOf(
fun genPublicApi() = buildString {
fun appendln(@Language("kt") code: String) {
this.appendln(code.trimIndent())
this.appendLine(code.trimIndent())
}
appendln(COPYRIGHT.trim())
appendln()
appendLine()
appendln(
"""
package net.mamoe.mirai.console.setting
@ -70,7 +70,7 @@ fun genPublicApi() = buildString {
import kotlin.reflect.KProperty
"""
)
appendln()
appendLine()
appendln(
"""
/**
@ -80,7 +80,7 @@ fun genPublicApi() = buildString {
*/
"""
)
appendln()
appendLine()
appendln(
"""
@ -107,7 +107,7 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
}
"""
)
appendln()
appendLine()
// PRIMITIVES
@ -127,7 +127,7 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
appendln(template)
}
appendln()
appendLine()
for (number in OTHER_PRIMITIVES) {
val template = """
@ -137,7 +137,7 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
appendln(template)
}
appendln()
appendLine()
// ARRAYS
@ -154,7 +154,7 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
sealed class PrimitiveArrayValue<T : Any> : ArrayValue<T>()
"""
)
appendln()
appendLine()
for (number in (NUMBERS + OTHER_PRIMITIVES).filterNot { it == "String" }) {
appendln(
@ -164,10 +164,10 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
}
"""
)
appendln()
appendLine()
}
appendln()
appendLine()
// TYPED ARRAYS
@ -178,7 +178,7 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
}
"""
)
appendln()
appendLine()
for (number in (NUMBERS + OTHER_PRIMITIVES)) {
appendln(
@ -188,7 +188,7 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
)
}
appendln()
appendLine()
// TYPED LISTS / SETS
for (collectionName in listOf("List", "Set")) {
@ -207,14 +207,14 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
appendln(template)
}
appendln()
appendLine()
// SETTING
appendln(
"""
abstract class Setting${collectionName}Value<T: Setting> internal constructor() : Value<${collectionName}<T>>(), ${collectionName}<T>
"""
)
appendln()
appendLine()
}
// SETTING VALUE
@ -225,7 +225,7 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
"""
)
appendln()
appendLine()
// MUTABLE LIST / MUTABLE 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)) {
appendln(
@ -245,17 +245,17 @@ sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
)
}
appendln()
appendLine()
// SETTING
appendln(
"""
abstract class MutableSetting${collectionName}Value<T: Setting> internal constructor() : Value<Mutable${collectionName}<T>>(), Mutable${collectionName}<T>
"""
)
appendln()
appendLine()
}
appendln()
appendLine()
// DYNAMIC
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
*/
@file:Suppress("NOTHING_TO_INLINE", "MemberVisibilityCanBePrivate", "unused")
package net.mamoe.mirai.console.codegen
import org.intellij.lang.annotations.Language
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 END ////
@ -19,17 +99,11 @@ fun codegen(targetFile: String, regionName: String, block: StringBuilder.() -> U
println("Codegen target: ${it.absolutePath}")
}.apply {
writeText(
readText()
.replace(Regex("""//// region $regionName CODEGEN START ////([\s\S]*?)//// endregion $regionName CODEGEN END ////""")) {
val code = StringBuilder().apply(block).toString()
"""
|//// region $regionName CODEGEN START ////
|
|$code
|
|//// endregion $regionName CODEGEN END ////
""".trimMargin()
CodegenScope().apply(block).also { list ->
list.forEach {
println("Applying replacement: $it")
}
}.applyTo(readText())
)
}
}
@ -48,7 +122,7 @@ fun String.findFileSmart(): File = kotlin.run {
}
fun main() {
codegen("Value.kt", "PrimitiveValue") {
codegen("Value.kt") {
SettingCodegen.PrimitiveValuesCodegen()
}
}