mirror of
https://github.com/GayPizzaSpecifications/pork.git
synced 2025-08-03 13:11:32 +00:00
ast: utilize extension functions to prevent larger stack frames from default interface methods
This commit is contained in:
@ -4,10 +4,7 @@ import org.gradle.api.JavaVersion
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.JavaPluginExtension
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
import org.gradle.kotlin.dsl.getByType
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
import org.gradle.kotlin.dsl.*
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
open class PorkModulePlugin : Plugin<Project> {
|
||||
@ -16,6 +13,7 @@ open class PorkModulePlugin : Plugin<Project> {
|
||||
target.apply(plugin = "org.jetbrains.kotlin.plugin.serialization")
|
||||
|
||||
target.repositories.mavenCentral()
|
||||
target.repositories.maven(url = "https://gitlab.com/api/v4/projects/49101454/packages/maven")
|
||||
|
||||
target.extensions.getByType<JavaPluginExtension>().apply {
|
||||
val javaVersion = JavaVersion.toVersion(17)
|
||||
@ -30,6 +28,8 @@ open class PorkModulePlugin : Plugin<Project> {
|
||||
target.dependencies {
|
||||
add("implementation", "org.jetbrains.kotlin:kotlin-bom")
|
||||
add("implementation", "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
|
||||
add("api", "gay.pizza.dough:dough-core:0.1.0-SNAPSHOT")
|
||||
add("api", "gay.pizza.dough:dough-fs:0.1.0-SNAPSHOT")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -72,9 +72,32 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
||||
)
|
||||
visitorInterface.functions.add(visitFunction)
|
||||
}
|
||||
write("NodeVisitor.kt", KotlinWriter(visitorInterface))
|
||||
|
||||
val visitorExtensionSet = KotlinFunctionSet(pkg)
|
||||
val visitAnyFunction = KotlinFunction(
|
||||
"visit",
|
||||
typeParameters = mutableListOf("T"),
|
||||
extensionOf = "NodeVisitor<T>",
|
||||
returnType = "T",
|
||||
parameters = mutableListOf(
|
||||
KotlinParameter("node", type = "Node")
|
||||
),
|
||||
isImmediateExpression = true
|
||||
)
|
||||
visitAnyFunction.body.add("when (node) {")
|
||||
for (type in world.typeRegistry.types.filter {
|
||||
world.typeRegistry.roleOfType(it) == AstTypeRole.AstNode
|
||||
}) {
|
||||
visitAnyFunction.body.add(" is ${type.name} -> visit${type.name}(node)")
|
||||
}
|
||||
visitAnyFunction.body.add("}")
|
||||
visitorExtensionSet.functions.add(visitAnyFunction)
|
||||
|
||||
val visitNodesFunction = KotlinFunction(
|
||||
"visitNodes",
|
||||
typeParameters = mutableListOf("T"),
|
||||
extensionOf = "NodeVisitor<T>",
|
||||
returnType = "List<T>",
|
||||
parameters = mutableListOf(
|
||||
KotlinParameter("nodes", type = "Node?", vararg = true)
|
||||
@ -82,10 +105,12 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
||||
isImmediateExpression = true
|
||||
)
|
||||
visitNodesFunction.body.add("nodes.asSequence().filterNotNull().map { visit(it) }.toList()")
|
||||
visitorInterface.functions.add(visitNodesFunction)
|
||||
visitorExtensionSet.functions.add(visitNodesFunction)
|
||||
|
||||
val visitAllFunction = KotlinFunction(
|
||||
"visitAll",
|
||||
typeParameters = mutableListOf("T"),
|
||||
extensionOf = "NodeVisitor<T>",
|
||||
returnType = "List<T>",
|
||||
parameters = mutableListOf(
|
||||
KotlinParameter("nodeLists", type = "List<Node>", vararg = true)
|
||||
@ -93,25 +118,9 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
||||
isImmediateExpression = true
|
||||
)
|
||||
visitAllFunction.body.add("nodeLists.asSequence().flatten().map { visit(it) }.toList()")
|
||||
visitorInterface.functions.add(visitAllFunction)
|
||||
visitorExtensionSet.functions.add(visitAllFunction)
|
||||
|
||||
val visitFunction = KotlinFunction(
|
||||
"visit",
|
||||
returnType = "T",
|
||||
parameters = mutableListOf(
|
||||
KotlinParameter("node", type = "Node")
|
||||
),
|
||||
isImmediateExpression = true
|
||||
)
|
||||
|
||||
visitFunction.body.add("when (node) {")
|
||||
for (type in world.typeRegistry.types.filter { world.typeRegistry.roleOfType(it) == AstTypeRole.AstNode }) {
|
||||
visitFunction.body.add(" is ${type.name} -> visit${type.name}(node)")
|
||||
}
|
||||
visitFunction.body.add("}")
|
||||
visitorInterface.functions.add(visitFunction)
|
||||
|
||||
write("NodeVisitor.kt", KotlinWriter(visitorInterface))
|
||||
write("NodeVisitorExtensions.kt", KotlinWriter(visitorExtensionSet))
|
||||
}
|
||||
|
||||
private fun writeNodeCoalescer() {
|
||||
@ -193,10 +202,21 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
||||
),
|
||||
isImmediateExpression = true
|
||||
)
|
||||
|
||||
abstractVisitChildrenFunction.body.add("emptyList()")
|
||||
|
||||
kotlinClassLike.functions.add(abstractVisitChildrenFunction)
|
||||
|
||||
val abstractVisitSelfFunction = KotlinFunction(
|
||||
"visit",
|
||||
returnType = "T",
|
||||
open = true,
|
||||
typeParameters = mutableListOf("T"),
|
||||
parameters = mutableListOf(
|
||||
KotlinParameter("visitor", "NodeVisitor<T>")
|
||||
),
|
||||
isImmediateExpression = true
|
||||
)
|
||||
abstractVisitSelfFunction.body.add("visitor.visit(this)")
|
||||
kotlinClassLike.functions.add(abstractVisitSelfFunction)
|
||||
} else if (role == AstTypeRole.AstNode) {
|
||||
val typeMember = KotlinMember(
|
||||
"type",
|
||||
@ -245,21 +265,21 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
||||
val visitChildrenFunction = KotlinFunction(
|
||||
"visitChildren",
|
||||
returnType = "List<T>",
|
||||
typeParameters = mutableListOf("T")
|
||||
typeParameters = mutableListOf("T"),
|
||||
overridden = true,
|
||||
parameters = mutableListOf(
|
||||
KotlinParameter("visitor", "NodeVisitor<T>")
|
||||
),
|
||||
isImmediateExpression = true
|
||||
)
|
||||
visitChildrenFunction.overridden = true
|
||||
val visitorParameter = KotlinParameter("visitor", "NodeVisitor<T>")
|
||||
visitChildrenFunction.parameters.add(visitorParameter)
|
||||
|
||||
visitChildrenFunction.isImmediateExpression = true
|
||||
|
||||
val anyListMembers = type.values.any { it.typeRef.form == AstTypeRefForm.List }
|
||||
val elideVisitChildren: Boolean
|
||||
if (anyListMembers) {
|
||||
val visitParameters = type.values.mapNotNull {
|
||||
if (it.typeRef.primitive != null) {
|
||||
null
|
||||
} else if (it.typeRef.type != null && !world.typeRegistry.roleOfType(it.typeRef.type).isNodeInherited()) {
|
||||
} else if (it.typeRef.type != null &&
|
||||
!world.typeRegistry.roleOfType(it.typeRef.type).isNodeInherited()) {
|
||||
null
|
||||
} else if (it.typeRef.form == AstTypeRefForm.Single) {
|
||||
"listOf(${it.name})"
|
||||
@ -273,7 +293,8 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
||||
val visitParameters = type.values.mapNotNull {
|
||||
if (it.typeRef.primitive != null) {
|
||||
null
|
||||
} else if (it.typeRef.type != null && !world.typeRegistry.roleOfType(it.typeRef.type).isNodeInherited()) {
|
||||
} else if (it.typeRef.type != null &&
|
||||
!world.typeRegistry.roleOfType(it.typeRef.type).isNodeInherited()) {
|
||||
null
|
||||
} else {
|
||||
it.name
|
||||
@ -287,7 +308,22 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
||||
kotlinClassLike.functions.add(visitChildrenFunction)
|
||||
}
|
||||
|
||||
val equalsAndHashCodeMembers = kotlinClassLike.members.map { it.name }.sortedBy { it == "type" }
|
||||
val visitSelfFunction = KotlinFunction(
|
||||
"visit",
|
||||
returnType = "T",
|
||||
typeParameters = mutableListOf("T"),
|
||||
overridden = true,
|
||||
parameters = mutableListOf(
|
||||
KotlinParameter("visitor", "NodeVisitor<T>")
|
||||
),
|
||||
isImmediateExpression = true
|
||||
)
|
||||
visitSelfFunction.body.add("visitor.visit${type.name}(this)")
|
||||
kotlinClassLike.functions.add(visitSelfFunction)
|
||||
|
||||
val equalsAndHashCodeMembers = kotlinClassLike.members.map {
|
||||
it.name
|
||||
}.sortedBy { it == "type" }
|
||||
val equalsFunction = KotlinFunction(
|
||||
"equals",
|
||||
returnType = "Boolean",
|
||||
@ -348,9 +384,10 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
||||
}
|
||||
|
||||
private fun write(fileName: String, writer: KotlinWriter) {
|
||||
val content = "// GENERATED CODE FROM PORK AST CODEGEN\n$writer"
|
||||
val path = outputDirectory.resolve(fileName)
|
||||
path.deleteIfExists()
|
||||
path.writeText(writer.toString(), StandardCharsets.UTF_8)
|
||||
path.writeText(content, StandardCharsets.UTF_8)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -3,6 +3,7 @@ package gay.pizza.pork.buildext.codegen
|
||||
class KotlinFunction(
|
||||
val name: String,
|
||||
var typeParameters: MutableList<String> = mutableListOf(),
|
||||
var extensionOf: String? = null,
|
||||
var parameters: MutableList<KotlinParameter> = mutableListOf(),
|
||||
var returnType: String? = null,
|
||||
var abstract: Boolean = false,
|
||||
|
@ -0,0 +1,7 @@
|
||||
package gay.pizza.pork.buildext.codegen
|
||||
|
||||
class KotlinFunctionSet(
|
||||
val pkg: String,
|
||||
var imports: MutableList<String> = mutableListOf(),
|
||||
var functions: MutableList<KotlinFunction> = mutableListOf()
|
||||
)
|
@ -3,8 +3,8 @@ package gay.pizza.pork.buildext.codegen
|
||||
class KotlinWriter() {
|
||||
private val buffer = StringBuilder()
|
||||
|
||||
constructor(kotlinClassLike: KotlinClassLike) : this() {
|
||||
write(kotlinClassLike)
|
||||
constructor(writable: Any) : this() {
|
||||
write(writable)
|
||||
}
|
||||
|
||||
fun writeClass(kotlinClass: KotlinClass): Unit = buffer.run {
|
||||
@ -96,16 +96,7 @@ class KotlinWriter() {
|
||||
classType: String,
|
||||
kotlinClass: KotlinClassLike
|
||||
): Unit = buffer.run {
|
||||
appendLine("package ${kotlinClass.pkg}")
|
||||
appendLine()
|
||||
|
||||
for (import in kotlinClass.imports) {
|
||||
appendLine("import $import")
|
||||
}
|
||||
|
||||
if (kotlinClass.imports.isNotEmpty()) {
|
||||
appendLine()
|
||||
}
|
||||
writeHeader(kotlinClass.pkg, kotlinClass.imports)
|
||||
|
||||
for (annotation in kotlinClass.annotations) {
|
||||
appendLine("@${annotation}")
|
||||
@ -138,76 +129,110 @@ class KotlinWriter() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeFunctions(kotlinClassLike: KotlinClassLike): Unit = buffer.run {
|
||||
for ((index, function) in kotlinClassLike.functions.withIndex()) {
|
||||
append(" ")
|
||||
private fun writeHeader(pkg: String, imports: List<String>): Unit = buffer.run {
|
||||
appendLine("package $pkg")
|
||||
appendLine()
|
||||
|
||||
if (function.overridden) {
|
||||
append("override ")
|
||||
}
|
||||
for (import in imports) {
|
||||
appendLine("import $import")
|
||||
}
|
||||
|
||||
if (function.abstract) {
|
||||
append("abstract ")
|
||||
}
|
||||
|
||||
if (function.open) {
|
||||
append("open ")
|
||||
}
|
||||
|
||||
append("fun ")
|
||||
if (function.typeParameters.isNotEmpty()) {
|
||||
append("<${function.typeParameters.joinToString(", ")}> ")
|
||||
}
|
||||
append("${function.name}(")
|
||||
append(function.parameters.joinToString(", ") {
|
||||
var start = "${it.name}: ${it.type}"
|
||||
if (it.vararg) {
|
||||
start = "vararg $start"
|
||||
}
|
||||
if (it.defaultValue != null) {
|
||||
start + " = ${it.defaultValue}"
|
||||
} else start
|
||||
})
|
||||
append(")")
|
||||
if (function.returnType != null) {
|
||||
append(": ${function.returnType}")
|
||||
}
|
||||
|
||||
if (!function.isImmediateExpression && !function.abstract && !function.isInterfaceMethod) {
|
||||
append(" {")
|
||||
} else if (!function.abstract && !function.isInterfaceMethod) {
|
||||
append(" =")
|
||||
}
|
||||
|
||||
if (function.body.isNotEmpty()) {
|
||||
appendLine()
|
||||
|
||||
for (item in function.body) {
|
||||
appendLine(" $item")
|
||||
}
|
||||
}
|
||||
|
||||
if (!function.isImmediateExpression && !function.abstract && !function.isInterfaceMethod) {
|
||||
if (function.body.isNotEmpty()) {
|
||||
append(" ")
|
||||
}
|
||||
appendLine("}")
|
||||
}
|
||||
|
||||
if (function.abstract || function.isInterfaceMethod) {
|
||||
appendLine()
|
||||
}
|
||||
|
||||
if (index < kotlinClassLike.functions.size - 1) {
|
||||
appendLine()
|
||||
}
|
||||
if (imports.isNotEmpty()) {
|
||||
appendLine()
|
||||
}
|
||||
}
|
||||
|
||||
fun write(input: KotlinClassLike): Unit = when (input) {
|
||||
fun writeFunction(function: KotlinFunction, index: Int = 0, functionCount: Int = 1, indent: String = ""): Unit = buffer.run {
|
||||
append(indent)
|
||||
|
||||
if (function.overridden) {
|
||||
append("override ")
|
||||
}
|
||||
|
||||
if (function.abstract) {
|
||||
append("abstract ")
|
||||
}
|
||||
|
||||
if (function.open) {
|
||||
append("open ")
|
||||
}
|
||||
|
||||
append("fun ")
|
||||
if (function.typeParameters.isNotEmpty()) {
|
||||
append("<${function.typeParameters.joinToString(", ")}> ")
|
||||
}
|
||||
|
||||
if (function.extensionOf != null) {
|
||||
append("${function.extensionOf}.")
|
||||
}
|
||||
|
||||
append("${function.name}(")
|
||||
append(function.parameters.joinToString(", ") {
|
||||
var start = "${it.name}: ${it.type}"
|
||||
if (it.vararg) {
|
||||
start = "vararg $start"
|
||||
}
|
||||
if (it.defaultValue != null) {
|
||||
start + " = ${it.defaultValue}"
|
||||
} else start
|
||||
})
|
||||
append(")")
|
||||
if (function.returnType != null) {
|
||||
append(": ${function.returnType}")
|
||||
}
|
||||
|
||||
if (!function.isImmediateExpression && !function.abstract && !function.isInterfaceMethod) {
|
||||
append(" {")
|
||||
} else if (!function.abstract && !function.isInterfaceMethod) {
|
||||
append(" =")
|
||||
}
|
||||
|
||||
if (function.body.isNotEmpty()) {
|
||||
appendLine()
|
||||
|
||||
for (item in function.body) {
|
||||
appendLine("$indent $item")
|
||||
}
|
||||
}
|
||||
|
||||
if (!function.isImmediateExpression && !function.abstract && !function.isInterfaceMethod) {
|
||||
if (function.body.isNotEmpty()) {
|
||||
append(indent)
|
||||
}
|
||||
appendLine("}")
|
||||
}
|
||||
|
||||
if (function.abstract || function.isInterfaceMethod) {
|
||||
appendLine()
|
||||
}
|
||||
|
||||
if (index < functionCount - 1) {
|
||||
appendLine()
|
||||
}
|
||||
}
|
||||
|
||||
fun writeFunctions(kotlinClassLike: KotlinClassLike): Unit = buffer.run {
|
||||
for ((index, function) in kotlinClassLike.functions.withIndex()) {
|
||||
writeFunction(function, index = index, functionCount = kotlinClassLike.functions.size, indent = " ")
|
||||
}
|
||||
}
|
||||
|
||||
fun writeFunctionSet(functionSet: KotlinFunctionSet) {
|
||||
writeHeader(functionSet.pkg, functionSet.imports)
|
||||
for ((index, function) in functionSet.functions.withIndex()) {
|
||||
writeFunction(
|
||||
function,
|
||||
index = index,
|
||||
functionCount = functionSet.functions.size
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun write(input: Any): Unit = when (input) {
|
||||
is KotlinClass -> writeClass(input)
|
||||
is KotlinEnum -> writeEnum(input)
|
||||
else -> throw RuntimeException("Unknown Kotlin Class Type")
|
||||
is KotlinFunctionSet -> writeFunctionSet(input)
|
||||
else -> throw RuntimeException("Unknown Kotlin Type")
|
||||
}
|
||||
|
||||
override fun toString(): String = buffer.toString()
|
||||
|
Reference in New Issue
Block a user