diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0ebcd99..cd40f3c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,3 +15,13 @@ jobs: uses: gradle/gradle-build-action@v2 with: arguments: build + - name: Archive Pork Bundle + uses: actions/upload-artifact@v3 + with: + name: pork-bundle + path: tool/build/distributions/pork.zip + - name: Archive Pork Jar + uses: actions/upload-artifact@v3 + with: + name: pork-jar + path: tool/build/distributions/pork-all.jar diff --git a/.github/workflows/graal.yml b/.github/workflows/graal.yml new file mode 100644 index 0000000..5f43c93 --- /dev/null +++ b/.github/workflows/graal.yml @@ -0,0 +1,22 @@ +name: graal +on: [push] +jobs: + linux-amd64: + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v3 + - name: Set up GraalVM + uses: graalvm/setup-graalvm@v1 + with: + java-version: '17' + distribution: 'graalvm' + - name: Build with Gradle + uses: gradle/gradle-build-action@v2 + with: + arguments: nativeCompile + - name: Archive Pork Executable + uses: actions/upload-artifact@v3 + with: + name: pork-linux-amd64 + path: tool/build/native/nativeCompile/pork diff --git a/ast/build.gradle.kts b/ast/build.gradle.kts index 57e6d8c..a60342f 100644 --- a/ast/build.gradle.kts +++ b/ast/build.gradle.kts @@ -1,5 +1,4 @@ plugins { - pork_module - + id("gay.pizza.pork.module") id("gay.pizza.pork.ast") } diff --git a/ast/src/main/ast/pork.yml b/ast/src/main/ast/pork.yml index 0ddd396..35e796c 100644 --- a/ast/src/main/ast/pork.yml +++ b/ast/src/main/ast/pork.yml @@ -33,7 +33,7 @@ types: - name: declarations type: List - name: definitions - type: List + type: List LetAssignment: parent: Expression values: diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/CompilationUnit.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/CompilationUnit.kt index 779f8cb..d08b249 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/CompilationUnit.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/CompilationUnit.kt @@ -9,15 +9,16 @@ class CompilationUnit(val declarations: List, val definitions: List override val type: NodeType = NodeType.CompilationUnit override fun visitChildren(visitor: NodeVisitor): List = - visitor.visitAll(declarations) + visitor.visitAll(declarations, definitions) override fun equals(other: Any?): Boolean { if (other !is CompilationUnit) return false - return other.declarations == declarations + return other.declarations == declarations && other.definitions == definitions } override fun hashCode(): Int { var result = declarations.hashCode() + result = 31 * result + definitions.hashCode() result = 31 * result + type.hashCode() return result } diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/Declaration.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/Declaration.kt index 79dbdeb..c1d23d6 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/Declaration.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/Declaration.kt @@ -1,6 +1,8 @@ package gay.pizza.pork.ast +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable +@SerialName("declaration") sealed class Declaration : Node() diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/Definition.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/Definition.kt index e321346..e92792a 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/Definition.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/Definition.kt @@ -1,8 +1,10 @@ package gay.pizza.pork.ast +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable +@SerialName("definition") sealed class Definition : Node() { abstract val symbol: Symbol abstract val modifiers: DefinitionModifiers diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/DefinitionModifiers.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/DefinitionModifiers.kt index 62b31a7..b3b570e 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/DefinitionModifiers.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/DefinitionModifiers.kt @@ -1,8 +1,8 @@ package gay.pizza.pork.ast +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -class DefinitionModifiers( - var export: Boolean -) +@SerialName("definitionModifiers") +class DefinitionModifiers(var export: Boolean) diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/Expression.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/Expression.kt index a4c6a00..d8497bc 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/Expression.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/Expression.kt @@ -1,6 +1,8 @@ package gay.pizza.pork.ast +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable +@SerialName("expression") sealed class Expression : Node() diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/FunctionDefinition.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/FunctionDefinition.kt index 663f707..46424ef 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/FunctionDefinition.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/FunctionDefinition.kt @@ -5,24 +5,20 @@ import kotlinx.serialization.Serializable @Serializable @SerialName("functionDefinition") -class FunctionDefinition( - override val modifiers: DefinitionModifiers, - override val symbol: Symbol, - val arguments: List, - val block: Block -) : Definition() { +class FunctionDefinition(override val modifiers: DefinitionModifiers, override val symbol: Symbol, val arguments: List, val block: Block) : Definition() { override val type: NodeType = NodeType.FunctionDefinition override fun visitChildren(visitor: NodeVisitor): List = - visitor.visitNodes(symbol) + visitor.visitAll(listOf(symbol), arguments, listOf(block)) override fun equals(other: Any?): Boolean { if (other !is FunctionDefinition) return false - return other.symbol == symbol && other.arguments == arguments && other.block == block + return other.modifiers == modifiers && other.symbol == symbol && other.arguments == arguments && other.block == block } override fun hashCode(): Int { - var result = symbol.hashCode() + var result = modifiers.hashCode() + result = 31 * result + symbol.hashCode() result = 31 * result + arguments.hashCode() result = 31 * result + block.hashCode() result = 31 * result + type.hashCode() diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/If.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/If.kt index ab671d1..090e4af 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/If.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/If.kt @@ -5,11 +5,7 @@ import kotlinx.serialization.Serializable @Serializable @SerialName("if") -class If( - val condition: Expression, - val thenExpression: Expression, - val elseExpression: Expression? = null -) : Expression() { +class If(val condition: Expression, val thenExpression: Expression, val elseExpression: Expression?) : Expression() { override val type: NodeType = NodeType.If override fun visitChildren(visitor: NodeVisitor): List = @@ -17,15 +13,13 @@ class If( override fun equals(other: Any?): Boolean { if (other !is If) return false - return other.condition == condition && - other.thenExpression == thenExpression && - other.elseExpression == elseExpression + return other.condition == condition && other.thenExpression == thenExpression && other.elseExpression == elseExpression } override fun hashCode(): Int { var result = condition.hashCode() result = 31 * result + thenExpression.hashCode() - result = 31 * result + (elseExpression?.hashCode() ?: 0) + result = 31 * result + elseExpression.hashCode() result = 31 * result + type.hashCode() return result } diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/InfixOperation.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/InfixOperation.kt index 22bc0fc..e77a6a0 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/InfixOperation.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/InfixOperation.kt @@ -5,11 +5,7 @@ import kotlinx.serialization.Serializable @Serializable @SerialName("infixOperation") -class InfixOperation( - val left: Expression, - val op: InfixOperator, - val right: Expression -) : Expression() { +class InfixOperation(val left: Expression, val op: InfixOperator, val right: Expression) : Expression() { override val type: NodeType = NodeType.InfixOperation override fun visitChildren(visitor: NodeVisitor): List = @@ -17,9 +13,7 @@ class InfixOperation( override fun equals(other: Any?): Boolean { if (other !is InfixOperation) return false - return other.op == op && - other.left == left && - other.right == right + return other.left == left && other.op == op && other.right == right } override fun hashCode(): Int { diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/IntLiteral.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/IntLiteral.kt index c6c3e0c..fc33523 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/IntLiteral.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/IntLiteral.kt @@ -14,7 +14,7 @@ class IntLiteral(val value: Int) : Expression() { } override fun hashCode(): Int { - var result = value + var result = value.hashCode() result = 31 * result + type.hashCode() return result } diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/Node.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/Node.kt index 54bb3fa..9063c01 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/Node.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/Node.kt @@ -1,9 +1,13 @@ package gay.pizza.pork.ast +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable +@SerialName("node") sealed class Node { abstract val type: NodeType - open fun visitChildren(visitor: NodeVisitor): List = emptyList() + + open fun visitChildren(visitor: NodeVisitor): List = + emptyList() } diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeCoalescer.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeCoalescer.kt index ebe2d0a..30e7b97 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeCoalescer.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeCoalescer.kt @@ -1,26 +1,58 @@ package gay.pizza.pork.ast class NodeCoalescer(val handler: (Node) -> Unit) : NodeVisitor { - override fun visitIntLiteral(node: IntLiteral): Unit = handle(node) - override fun visitStringLiteral(node: StringLiteral): Unit = handle(node) - override fun visitBooleanLiteral(node: BooleanLiteral): Unit = handle(node) - override fun visitListLiteral(node: ListLiteral): Unit = handle(node) - override fun visitSymbol(node: Symbol): Unit = handle(node) - override fun visitFunctionCall(node: FunctionCall): Unit = handle(node) - override fun visitLetAssignment(node: LetAssignment): Unit = handle(node) - override fun visitSymbolReference(node: SymbolReference): Unit = handle(node) - override fun visitLambda(node: Lambda): Unit = handle(node) - override fun visitParentheses(node: Parentheses): Unit = handle(node) - override fun visitPrefixOperation(node: PrefixOperation): Unit = handle(node) - override fun visitIf(node: If): Unit = handle(node) - override fun visitInfixOperation(node: InfixOperation): Unit = handle(node) - override fun visitFunctionDeclaration(node: FunctionDefinition): Unit = handle(node) - override fun visitBlock(node: Block): Unit = handle(node) - override fun visitImportDeclaration(node: ImportDeclaration): Unit = handle(node) + override fun visitBlock(node: Block): Unit = + handle(node) - override fun visitCompilationUnit(node: CompilationUnit): Unit = handle(node) + override fun visitBooleanLiteral(node: BooleanLiteral): Unit = + handle(node) - private fun handle(node: Node) { + override fun visitCompilationUnit(node: CompilationUnit): Unit = + handle(node) + + override fun visitFunctionCall(node: FunctionCall): Unit = + handle(node) + + override fun visitFunctionDefinition(node: FunctionDefinition): Unit = + handle(node) + + override fun visitIf(node: If): Unit = + handle(node) + + override fun visitImportDeclaration(node: ImportDeclaration): Unit = + handle(node) + + override fun visitInfixOperation(node: InfixOperation): Unit = + handle(node) + + override fun visitIntLiteral(node: IntLiteral): Unit = + handle(node) + + override fun visitLambda(node: Lambda): Unit = + handle(node) + + override fun visitLetAssignment(node: LetAssignment): Unit = + handle(node) + + override fun visitListLiteral(node: ListLiteral): Unit = + handle(node) + + override fun visitParentheses(node: Parentheses): Unit = + handle(node) + + override fun visitPrefixOperation(node: PrefixOperation): Unit = + handle(node) + + override fun visitStringLiteral(node: StringLiteral): Unit = + handle(node) + + override fun visitSymbol(node: Symbol): Unit = + handle(node) + + override fun visitSymbolReference(node: SymbolReference): Unit = + handle(node) + + fun handle(node: Node) { handler(node) node.visitChildren(this) } diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeType.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeType.kt index ac98843..cd5bb3b 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeType.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeType.kt @@ -2,24 +2,24 @@ package gay.pizza.pork.ast enum class NodeType(val parent: NodeType? = null) { Node, - Symbol(Node), + Block(Node), Expression(Node), + BooleanLiteral(Expression), + CompilationUnit(Node), Declaration(Node), Definition(Node), - Block(Node), - CompilationUnit(Node), - IntLiteral(Expression), - BooleanLiteral(Expression), - ListLiteral(Expression), - StringLiteral(Expression), - Parentheses(Expression), - LetAssignment(Expression), - Lambda(Expression), - PrefixOperation(Expression), - InfixOperation(Expression), - SymbolReference(Expression), FunctionCall(Expression), + FunctionDefinition(Definition), If(Expression), ImportDeclaration(Declaration), - FunctionDefinition(Definition) + InfixOperation(Expression), + IntLiteral(Expression), + Lambda(Expression), + LetAssignment(Expression), + ListLiteral(Expression), + Parentheses(Expression), + PrefixOperation(Expression), + StringLiteral(Expression), + Symbol(Node), + SymbolReference(Expression) } diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitor.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitor.kt index 4ad160c..6073352 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitor.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitor.kt @@ -1,60 +1,64 @@ package gay.pizza.pork.ast interface NodeVisitor { - fun visitIntLiteral(node: IntLiteral): T - fun visitStringLiteral(node: StringLiteral): T - fun visitBooleanLiteral(node: BooleanLiteral): T - fun visitListLiteral(node: ListLiteral): T - fun visitSymbol(node: Symbol): T - fun visitFunctionCall(node: FunctionCall): T - fun visitLetAssignment(node: LetAssignment): T - fun visitSymbolReference(node: SymbolReference): T - fun visitLambda(node: Lambda): T - fun visitParentheses(node: Parentheses): T - fun visitPrefixOperation(node: PrefixOperation): T - fun visitIf(node: If): T - fun visitInfixOperation(node: InfixOperation): T - fun visitFunctionDeclaration(node: FunctionDefinition): T fun visitBlock(node: Block): T - fun visitImportDeclaration(node: ImportDeclaration): T + fun visitBooleanLiteral(node: BooleanLiteral): T + fun visitCompilationUnit(node: CompilationUnit): T - fun visitExpression(node: Expression): T = when (node) { - is IntLiteral -> visitIntLiteral(node) - is StringLiteral -> visitStringLiteral(node) - is BooleanLiteral -> visitBooleanLiteral(node) - is ListLiteral -> visitListLiteral(node) - is FunctionCall -> visitFunctionCall(node) - is LetAssignment -> visitLetAssignment(node) - is SymbolReference -> visitSymbolReference(node) - is Lambda -> visitLambda(node) - is Parentheses -> visitParentheses(node) - is PrefixOperation -> visitPrefixOperation(node) - is If -> visitIf(node) - is InfixOperation -> visitInfixOperation(node) - } + fun visitFunctionCall(node: FunctionCall): T - fun visitDeclaration(node: Declaration): T = when (node) { - is ImportDeclaration -> visitImportDeclaration(node) - } + fun visitFunctionDefinition(node: FunctionDefinition): T - fun visitDefinition(node: Definition): T = when (node) { - is FunctionDefinition -> visitFunctionDeclaration(node) - } + fun visitIf(node: If): T - fun visit(node: Node): T = when (node) { - is Symbol -> visitSymbol(node) - is Expression -> visitExpression(node) - is CompilationUnit -> visitCompilationUnit(node) - is Block -> visitBlock(node) - is Declaration -> visitDeclaration(node) - is Definition -> visitDefinition(node) - } + fun visitImportDeclaration(node: ImportDeclaration): T + + fun visitInfixOperation(node: InfixOperation): T + + fun visitIntLiteral(node: IntLiteral): T + + fun visitLambda(node: Lambda): T + + fun visitLetAssignment(node: LetAssignment): T + + fun visitListLiteral(node: ListLiteral): T + + fun visitParentheses(node: Parentheses): T + + fun visitPrefixOperation(node: PrefixOperation): T + + fun visitStringLiteral(node: StringLiteral): T + + fun visitSymbol(node: Symbol): T + + fun visitSymbolReference(node: SymbolReference): T fun visitNodes(vararg nodes: Node?): List = nodes.asSequence().filterNotNull().map { visit(it) }.toList() fun visitAll(vararg nodeLists: List): List = nodeLists.asSequence().flatten().map { visit(it) }.toList() + + fun visit(node: Node): T = + when (node) { + is Symbol -> visitSymbol(node) + is Block -> visitBlock(node) + is CompilationUnit -> visitCompilationUnit(node) + is LetAssignment -> visitLetAssignment(node) + is InfixOperation -> visitInfixOperation(node) + is BooleanLiteral -> visitBooleanLiteral(node) + is FunctionCall -> visitFunctionCall(node) + is FunctionDefinition -> visitFunctionDefinition(node) + is If -> visitIf(node) + is ImportDeclaration -> visitImportDeclaration(node) + is IntLiteral -> visitIntLiteral(node) + is Lambda -> visitLambda(node) + is ListLiteral -> visitListLiteral(node) + is Parentheses -> visitParentheses(node) + is PrefixOperation -> visitPrefixOperation(node) + is StringLiteral -> visitStringLiteral(node) + is SymbolReference -> visitSymbolReference(node) + } } diff --git a/build.gradle.kts b/build.gradle.kts index 15c825c..684b6a1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,3 +1,8 @@ +plugins { + kotlin("jvm") version "1.9.10" apply false + kotlin("plugin.serialization") version "1.9.10" apply false +} + tasks.withType { gradleVersion = "8.3" } diff --git a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstCodegen.kt b/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstCodegen.kt deleted file mode 100644 index 6f19a3a..0000000 --- a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstCodegen.kt +++ /dev/null @@ -1,182 +0,0 @@ -package gay.pizza.pork.gradle.ast - -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory -import com.fasterxml.jackson.module.kotlin.KotlinModule -import gay.pizza.pork.gradle.codegen.* -import java.nio.charset.StandardCharsets -import java.nio.file.Path -import kotlin.io.path.* - -class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld) { - private fun deleteAllContents() { - for (child in outputDirectory.listDirectoryEntries("*.kt")) { - child.deleteExisting() - } - } - - fun generate() { - deleteAllContents() - for (type in world.typeRegistry.types) { - writeAstType(type) - } - writeNodeType() - } - - private fun writeNodeType() { - val enumClass = KotlinEnum(pkg, "NodeType") - val parentMember = KotlinMember("parent", "NodeType?", value = "null") - enumClass.members.add(parentMember) - - val typesInNameOrder = world.typeRegistry.types.sortedBy { it.name } - val typesInDependencyOrder = mutableListOf() - for (type in typesInNameOrder) { - if (type.parent != null) { - if (!typesInDependencyOrder.contains(type.parent)) { - typesInDependencyOrder.add(type.parent!!) - } - } - - if (!typesInDependencyOrder.contains(type)) { - typesInDependencyOrder.add(type) - } - } - - for (type in typesInDependencyOrder) { - val role = world.typeRegistry.roleOfType(type) - - if (role == AstTypeRole.ValueHolder || role == AstTypeRole.Enum) { - println(type) - continue - } - - val entry = KotlinEnumEntry(type.name) - if (type.parent != null) { - entry.parameters.add(type.parent!!.name) - } - enumClass.entries.add(entry) - } - - write("NodeType.kt", KotlinWriter(enumClass)) - } - - private fun writeAstType(type: AstType) { - val role = world.typeRegistry.roleOfType(type) - - val kotlinClassLike: KotlinClassLike - if (role == AstTypeRole.Enum) { - val kotlinEnum = KotlinEnum(pkg, type.name) - kotlinClassLike = kotlinEnum - } else { - val kotlinClass = KotlinClass(pkg, type.name) - kotlinClassLike = kotlinClass - if (role == AstTypeRole.RootNode || role == AstTypeRole.HierarchyNode) { - kotlinClass.sealed = true - } - } - - if (role == AstTypeRole.RootNode) { - val typeMember = KotlinMember( - "type", - "NodeType", - abstract = true - ) - kotlinClassLike.members.add(typeMember) - } else if (role == AstTypeRole.AstNode) { - val typeMember = KotlinMember( - "type", - "NodeType", - overridden = true, - value = "NodeType.${type.name}" - ) - kotlinClassLike.members.add(typeMember) - } - - if (type.parent != null) { - val parentName = type.parent!!.name - kotlinClassLike.inherits.add("$parentName()") - } - - for (value in type.values) { - val member = KotlinMember(value.name, toKotlinType(value.typeRef)) - member.abstract = value.abstract - if (type.isParentAbstract(value)) { - member.overridden = true - } - kotlinClassLike.members.add(member) - } - - if (role == AstTypeRole.Enum) { - val kotlinEnum = kotlinClassLike as KotlinEnum - for (entry in type.enums) { - val orderOfKeys = entry.values.keys.sortedBy { key -> - kotlinClassLike.members.indexOfFirst { it.name == key } - } - - val parameters = mutableListOf() - for (key in orderOfKeys) { - val value = entry.values[key] ?: continue - parameters.add("\"${value}\"") - } - val enumEntry = KotlinEnumEntry(entry.name, parameters) - kotlinEnum.entries.add(enumEntry) - } - } - - if (role == AstTypeRole.AstNode) { - val equalsAndHashCodeFields = kotlinClassLike.members.map { it.name } - val equalsFunction = KotlinFunction( - "equals", - returnType = "Boolean", - overridden = true - ) - equalsFunction.parameters.add(KotlinParameter( - "other", - "Any?" - )) - equalsFunction.body.add("if (other !is ${type.name}) return false") - val predicate = equalsAndHashCodeFields.joinToString(" && ") { - "other.${it} == $it" - } - equalsFunction.body.add("return $predicate") - kotlinClassLike.functions.add(equalsFunction) - } - - val serialName = kotlinClassLike.name[0].lowercase() + - kotlinClassLike.name.substring(1) - kotlinClassLike.imports.add("kotlinx.serialization.SerialName") - kotlinClassLike.imports.add("kotlinx.serialization.Serializable") - kotlinClassLike.annotations.add("Serializable") - kotlinClassLike.annotations.add("SerialName(\"$serialName\")") - - write("${type.name}.kt", KotlinWriter(kotlinClassLike)) - } - - private fun toKotlinType(typeRef: AstTypeRef): String { - val baseType = typeRef.type?.name ?: typeRef.primitive?.id - ?: throw RuntimeException("Unable to determine base type.") - return when (typeRef.form) { - AstTypeRefForm.Single -> baseType - AstTypeRefForm.Nullable -> "${baseType}?" - AstTypeRefForm.List -> "List<${baseType}>" - } - } - - private fun write(fileName: String, writer: KotlinWriter) { - val path = outputDirectory.resolve(fileName) - path.deleteIfExists() - path.writeText(writer.toString(), StandardCharsets.UTF_8) - } - - companion object { - fun run(pkg: String, astDescriptionFile: Path, outputDirectory: Path) { - val astYamlText = astDescriptionFile.readText() - val mapper = ObjectMapper(YAMLFactory()) - mapper.registerModules(KotlinModule.Builder().build()) - val astDescription = mapper.readValue(astYamlText, AstDescription::class.java) - val world = AstWorld.build(astDescription) - val codegen = AstCodegen(pkg, outputDirectory, world) - codegen.generate() - } - } -} diff --git a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstTypeRole.kt b/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstTypeRole.kt deleted file mode 100644 index f6cc138..0000000 --- a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstTypeRole.kt +++ /dev/null @@ -1,9 +0,0 @@ -package gay.pizza.pork.gradle.ast - -enum class AstTypeRole { - RootNode, - HierarchyNode, - AstNode, - ValueHolder, - Enum -} diff --git a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/codegen/KotlinParameter.kt b/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/codegen/KotlinParameter.kt deleted file mode 100644 index 1118f69..0000000 --- a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/codegen/KotlinParameter.kt +++ /dev/null @@ -1,7 +0,0 @@ -package gay.pizza.pork.gradle.codegen - -class KotlinParameter( - val name: String, - val type: String, - val defaultValue: String? = null -) diff --git a/buildSrc/src/main/kotlin/pork_module.gradle.kts b/buildSrc/src/main/kotlin/pork_module.gradle.kts deleted file mode 100644 index 332cd1e..0000000 --- a/buildSrc/src/main/kotlin/pork_module.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - -plugins { - kotlin("jvm") - kotlin("plugin.serialization") -} - -repositories { - mavenCentral() -} - -java { - val javaVersion = JavaVersion.toVersion(17) - sourceCompatibility = javaVersion - targetCompatibility = javaVersion -} - -tasks.withType { - kotlinOptions.jvmTarget = "17" -} - -dependencies { - implementation("org.jetbrains.kotlin:kotlin-bom") - implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0") -} diff --git a/buildSrc/build.gradle.kts b/buildext/build.gradle.kts similarity index 69% rename from buildSrc/build.gradle.kts rename to buildext/build.gradle.kts index 5420502..7308b9d 100644 --- a/buildSrc/build.gradle.kts +++ b/buildext/build.gradle.kts @@ -20,10 +20,18 @@ gradlePlugin { plugins { create("pork_ast") { id = "gay.pizza.pork.ast" - implementationClass = "gay.pizza.pork.gradle.PorkAstPlugin" + implementationClass = "gay.pizza.pork.buildext.PorkAstPlugin" displayName = "Pork AST" description = "AST generation code for pork" } + + create("pork_module") { + id = "gay.pizza.pork.module" + implementationClass = "gay.pizza.pork.buildext.PorkModulePlugin" + + displayName = "Pork Module" + description = "Module convention for pork" + } } } diff --git a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/GenerateAstCode.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/GenerateAstCode.kt similarity index 81% rename from buildSrc/src/main/kotlin/gay/pizza/pork/gradle/GenerateAstCode.kt rename to buildext/src/main/kotlin/gay/pizza/pork/buildext/GenerateAstCode.kt index c47ae36..ff7d5bd 100644 --- a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/GenerateAstCode.kt +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/GenerateAstCode.kt @@ -1,6 +1,6 @@ -package gay.pizza.pork.gradle +package gay.pizza.pork.buildext -import gay.pizza.pork.gradle.ast.AstCodegen +import gay.pizza.pork.buildext.ast.AstCodegen import org.gradle.api.DefaultTask import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputFile @@ -17,10 +17,10 @@ open class GenerateAstCode : DefaultTask() { var astDescriptionFile: File = project.file("src/main/ast/pork.yml") @get:Input - var codePackage: String = "gay.pizza.pork.gen" + var codePackage: String = "gay.pizza.pork.ast" @get:OutputDirectory - var outputDirectory: File = project.file("src/main/kotlin/gay/pizza/pork/gen") + var outputDirectory: File = project.file("src/main/kotlin/gay/pizza/pork/ast") @TaskAction fun generate() { diff --git a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/PorkAstPlugin.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/PorkAstPlugin.kt similarity index 87% rename from buildSrc/src/main/kotlin/gay/pizza/pork/gradle/PorkAstPlugin.kt rename to buildext/src/main/kotlin/gay/pizza/pork/buildext/PorkAstPlugin.kt index abfe960..931baa0 100644 --- a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/PorkAstPlugin.kt +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/PorkAstPlugin.kt @@ -1,4 +1,4 @@ -package gay.pizza.pork.gradle +package gay.pizza.pork.buildext import org.gradle.api.Plugin import org.gradle.api.Project diff --git a/buildext/src/main/kotlin/gay/pizza/pork/buildext/PorkModulePlugin.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/PorkModulePlugin.kt new file mode 100644 index 0000000..ae17489 --- /dev/null +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/PorkModulePlugin.kt @@ -0,0 +1,35 @@ +package gay.pizza.pork.buildext + +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.jetbrains.kotlin.gradle.tasks.KotlinCompile + +open class PorkModulePlugin : Plugin { + override fun apply(target: Project) { + target.apply(plugin = "org.jetbrains.kotlin.jvm") + target.apply(plugin = "org.jetbrains.kotlin.plugin.serialization") + + target.repositories.mavenCentral() + + target.extensions.getByType().apply { + val javaVersion = JavaVersion.toVersion(17) + sourceCompatibility = javaVersion + targetCompatibility = javaVersion + } + + target.tasks.withType { + kotlinOptions.jvmTarget = "17" + } + + target.dependencies { + add("implementation", "org.jetbrains.kotlin:kotlin-bom") + add("implementation", "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0") + } + } +} diff --git a/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstCodegen.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstCodegen.kt new file mode 100644 index 0000000..cd048fa --- /dev/null +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstCodegen.kt @@ -0,0 +1,370 @@ +package gay.pizza.pork.buildext.ast + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory +import com.fasterxml.jackson.module.kotlin.KotlinModule +import gay.pizza.pork.buildext.codegen.* +import java.nio.charset.StandardCharsets +import java.nio.file.Path +import kotlin.io.path.* + +class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld) { + private fun deleteAllContents() { + for (child in outputDirectory.listDirectoryEntries("*.kt")) { + child.deleteExisting() + } + } + + fun generate() { + deleteAllContents() + for (type in world.typeRegistry.types) { + writeAstType(type) + } + writeNodeType() + writeNodeVisitor() + writeNodeCoalescer() + } + + private fun writeNodeType() { + val enumClass = KotlinEnum(pkg, "NodeType") + val parentMember = KotlinMember("parent", "NodeType?", value = "null") + enumClass.members.add(parentMember) + + for (type in world.typesInDependencyOrder()) { + val role = world.typeRegistry.roleOfType(type) + + if (role == AstTypeRole.ValueHolder || role == AstTypeRole.Enum) { + continue + } + + val entry = KotlinEnumEntry(type.name) + if (type.parent != null) { + entry.parameters.add(type.parent!!.name) + } + enumClass.entries.add(entry) + } + + write("NodeType.kt", KotlinWriter(enumClass)) + } + + private fun writeNodeVisitor() { + val visitorInterface = KotlinClass( + pkg, + "NodeVisitor", + typeParameters = mutableListOf("T"), + isInterface = true + ) + + for (type in world.typesInDependencyOrder()) { + val role = world.typeRegistry.roleOfType(type) + + if (role != AstTypeRole.AstNode) { + continue + } + + val visitFunction = KotlinFunction( + "visit${type.name}", + returnType = "T", + parameters = mutableListOf( + KotlinParameter("node", type.name) + ), + isInterfaceMethod = true + ) + visitorInterface.functions.add(visitFunction) + } + + val visitNodesFunction = KotlinFunction( + "visitNodes", + returnType = "List", + parameters = mutableListOf( + KotlinParameter("nodes", type = "Node?", vararg = true) + ), + isImmediateExpression = true + ) + visitNodesFunction.body.add("nodes.asSequence().filterNotNull().map { visit(it) }.toList()") + visitorInterface.functions.add(visitNodesFunction) + + val visitAllFunction = KotlinFunction( + "visitAll", + returnType = "List", + parameters = mutableListOf( + KotlinParameter("nodeLists", type = "List", vararg = true) + ), + isImmediateExpression = true + ) + visitAllFunction.body.add("nodeLists.asSequence().flatten().map { visit(it) }.toList()") + visitorInterface.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)) + } + + private fun writeNodeCoalescer() { + val coalescerClass = KotlinClass( + pkg, + "NodeCoalescer", + inherits = mutableListOf("NodeVisitor"), + members = mutableListOf( + KotlinMember( + "handler", + "(Node) -> Unit" + ) + ) + ) + + for (type in world.typesInDependencyOrder()) { + val role = world.typeRegistry.roleOfType(type) + + if (role != AstTypeRole.AstNode) { + continue + } + + val function = KotlinFunction( + "visit${type.name}", + returnType = "Unit", + parameters = mutableListOf( + KotlinParameter("node", type.name) + ), + isImmediateExpression = true, + overridden = true + ) + function.body.add("handle(node)") + coalescerClass.functions.add(function) + } + + val handleFunction = KotlinFunction( + "handle", + parameters = mutableListOf( + KotlinParameter("node", "Node") + ) + ) + handleFunction.body.add("handler(node)") + handleFunction.body.add("node.visitChildren(this)") + coalescerClass.functions.add(handleFunction) + + write("NodeCoalescer.kt", KotlinWriter(coalescerClass)) + } + + private fun writeAstType(type: AstType) { + val role = world.typeRegistry.roleOfType(type) + + val kotlinClassLike: KotlinClassLike + if (role == AstTypeRole.Enum) { + val kotlinEnum = KotlinEnum(pkg, type.name) + kotlinClassLike = kotlinEnum + } else { + val kotlinClass = KotlinClass(pkg, type.name) + kotlinClassLike = kotlinClass + if (role == AstTypeRole.RootNode || role == AstTypeRole.HierarchyNode) { + kotlinClass.sealed = true + } + } + + if (role == AstTypeRole.RootNode) { + val typeMember = KotlinMember( + "type", + "NodeType", + abstract = true + ) + kotlinClassLike.members.add(typeMember) + + val abstractVisitChildrenFunction = KotlinFunction( + "visitChildren", + returnType = "List", + open = true, + typeParameters = mutableListOf("T"), + parameters = mutableListOf( + KotlinParameter("visitor", "NodeVisitor") + ), + isImmediateExpression = true + ) + + abstractVisitChildrenFunction.body.add("emptyList()") + + kotlinClassLike.functions.add(abstractVisitChildrenFunction) + } else if (role == AstTypeRole.AstNode) { + val typeMember = KotlinMember( + "type", + "NodeType", + overridden = true, + value = "NodeType.${type.name}" + ) + kotlinClassLike.members.add(typeMember) + } + + if (type.parent != null) { + val parentName = type.parent!!.name + kotlinClassLike.inherits.add("$parentName()") + } + + for (value in type.values) { + val member = KotlinMember(value.name, toKotlinType(value.typeRef)) + member.abstract = value.abstract + if (type.isParentAbstract(value)) { + member.overridden = true + } + if (role == AstTypeRole.ValueHolder) { + member.mutable = true + } + kotlinClassLike.members.add(member) + } + + if (role == AstTypeRole.Enum) { + val kotlinEnum = kotlinClassLike as KotlinEnum + for (entry in type.enums) { + val orderOfKeys = entry.values.keys.sortedBy { key -> + kotlinClassLike.members.indexOfFirst { it.name == key } + } + + val parameters = mutableListOf() + for (key in orderOfKeys) { + val value = entry.values[key] ?: continue + parameters.add("\"${value}\"") + } + val enumEntry = KotlinEnumEntry(entry.name, parameters) + kotlinEnum.entries.add(enumEntry) + } + } + + if (role == AstTypeRole.AstNode) { + val visitChildrenFunction = KotlinFunction( + "visitChildren", + returnType = "List", + typeParameters = mutableListOf("T") + ) + visitChildrenFunction.overridden = true + val visitorParameter = KotlinParameter("visitor", "NodeVisitor") + 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()) { + null + } else if (it.typeRef.form == AstTypeRefForm.Single) { + "listOf(${it.name})" + } else { + it.name + } + }.joinToString(", ") + elideVisitChildren = visitParameters.isEmpty() + visitChildrenFunction.body.add("visitor.visitAll(${visitParameters})") + } else { + val visitParameters = type.values.mapNotNull { + if (it.typeRef.primitive != null) { + null + } else if (it.typeRef.type != null && !world.typeRegistry.roleOfType(it.typeRef.type).isNodeInherited()) { + null + } else { + it.name + } + }.joinToString(", ") + elideVisitChildren = visitParameters.isEmpty() + visitChildrenFunction.body.add("visitor.visitNodes(${visitParameters})") + } + + if (!elideVisitChildren) { + kotlinClassLike.functions.add(visitChildrenFunction) + } + + val equalsAndHashCodeMembers = kotlinClassLike.members.map { it.name }.sortedBy { it == "type" } + val equalsFunction = KotlinFunction( + "equals", + returnType = "Boolean", + overridden = true + ) + equalsFunction.parameters.add(KotlinParameter( + "other", + "Any?" + )) + equalsFunction.body.add("if (other !is ${type.name}) return false") + val predicate = equalsAndHashCodeMembers.mapNotNull { + if (it == "type") null else "other.${it} == $it" + }.joinToString(" && ") + equalsFunction.body.add("return $predicate") + kotlinClassLike.functions.add(equalsFunction) + + val hashCodeFunction = KotlinFunction( + "hashCode", + returnType = "Int", + overridden = true + ) + + if (equalsAndHashCodeMembers.size == 1) { + val member = equalsAndHashCodeMembers.single() + hashCodeFunction.isImmediateExpression = true + hashCodeFunction.body.add("31 * ${member}.hashCode()") + } else { + for ((index, value) in equalsAndHashCodeMembers.withIndex()) { + if (index == 0) { + hashCodeFunction.body.add("var result = ${value}.hashCode()") + } else { + hashCodeFunction.body.add("result = 31 * result + ${value}.hashCode()") + } + } + hashCodeFunction.body.add("return result") + } + kotlinClassLike.functions.add(hashCodeFunction) + } + + val serialName = kotlinClassLike.name[0].lowercase() + + kotlinClassLike.name.substring(1) + kotlinClassLike.imports.add("kotlinx.serialization.SerialName") + kotlinClassLike.imports.add("kotlinx.serialization.Serializable") + kotlinClassLike.annotations.add("Serializable") + kotlinClassLike.annotations.add("SerialName(\"$serialName\")") + + write("${type.name}.kt", KotlinWriter(kotlinClassLike)) + } + + private fun toKotlinType(typeRef: AstTypeRef): String { + val baseType = typeRef.type?.name ?: typeRef.primitive?.id + ?: throw RuntimeException("Unable to determine base type.") + return when (typeRef.form) { + AstTypeRefForm.Single -> baseType + AstTypeRefForm.Nullable -> "${baseType}?" + AstTypeRefForm.List -> "List<${baseType}>" + } + } + + private fun write(fileName: String, writer: KotlinWriter) { + val path = outputDirectory.resolve(fileName) + path.deleteIfExists() + path.writeText(writer.toString(), StandardCharsets.UTF_8) + } + + companion object { + fun run(pkg: String, astDescriptionFile: Path, outputDirectory: Path) { + if (!outputDirectory.exists()) { + outputDirectory.createDirectories() + } + val astYamlText = astDescriptionFile.readText() + val mapper = ObjectMapper(YAMLFactory()) + mapper.registerModules(KotlinModule.Builder().build()) + val astDescription = mapper.readValue(astYamlText, AstDescription::class.java) + val world = AstWorld.build(astDescription) + val codegen = AstCodegen(pkg, outputDirectory, world) + codegen.generate() + } + } +} diff --git a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstDescription.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstDescription.kt similarity index 72% rename from buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstDescription.kt rename to buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstDescription.kt index a4239c1..fa7258e 100644 --- a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstDescription.kt +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstDescription.kt @@ -1,4 +1,4 @@ -package gay.pizza.pork.gradle.ast +package gay.pizza.pork.buildext.ast data class AstDescription( val root: String, diff --git a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstEnum.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstEnum.kt similarity index 66% rename from buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstEnum.kt rename to buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstEnum.kt index 8aaee9e..ce5b25f 100644 --- a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstEnum.kt +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstEnum.kt @@ -1,4 +1,4 @@ -package gay.pizza.pork.gradle.ast +package gay.pizza.pork.buildext.ast class AstEnum( val name: String, diff --git a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstEnumDescription.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstEnumDescription.kt similarity index 69% rename from buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstEnumDescription.kt rename to buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstEnumDescription.kt index eb15f17..c828df2 100644 --- a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstEnumDescription.kt +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstEnumDescription.kt @@ -1,4 +1,4 @@ -package gay.pizza.pork.gradle.ast +package gay.pizza.pork.buildext.ast class AstEnumDescription( val name: String, diff --git a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstPrimitive.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstPrimitive.kt similarity index 74% rename from buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstPrimitive.kt rename to buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstPrimitive.kt index baf6965..973f1a7 100644 --- a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstPrimitive.kt +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstPrimitive.kt @@ -1,4 +1,4 @@ -package gay.pizza.pork.gradle.ast +package gay.pizza.pork.buildext.ast enum class AstPrimitive(val id: kotlin.String) { Boolean("Boolean"), diff --git a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstType.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstType.kt similarity index 96% rename from buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstType.kt rename to buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstType.kt index 65cbcea..1af6831 100644 --- a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstType.kt +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstType.kt @@ -1,4 +1,4 @@ -package gay.pizza.pork.gradle.ast +package gay.pizza.pork.buildext.ast class AstType(val name: String, var parent: AstType? = null) { private val internalValues = mutableListOf() diff --git a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstTypeDescription.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstTypeDescription.kt similarity index 82% rename from buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstTypeDescription.kt rename to buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstTypeDescription.kt index 92622d6..62e9573 100644 --- a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstTypeDescription.kt +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstTypeDescription.kt @@ -1,4 +1,4 @@ -package gay.pizza.pork.gradle.ast +package gay.pizza.pork.buildext.ast data class AstTypeDescription( val parent: String? = null, diff --git a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstTypeRef.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstTypeRef.kt similarity index 96% rename from buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstTypeRef.kt rename to buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstTypeRef.kt index 5d2439e..949425b 100644 --- a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstTypeRef.kt +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstTypeRef.kt @@ -1,4 +1,4 @@ -package gay.pizza.pork.gradle.ast +package gay.pizza.pork.buildext.ast class AstTypeRef( val type: AstType? = null, diff --git a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstTypeRefForm.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstTypeRefForm.kt similarity index 62% rename from buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstTypeRefForm.kt rename to buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstTypeRefForm.kt index 685e834..8acea00 100644 --- a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstTypeRefForm.kt +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstTypeRefForm.kt @@ -1,4 +1,4 @@ -package gay.pizza.pork.gradle.ast +package gay.pizza.pork.buildext.ast enum class AstTypeRefForm { Single, diff --git a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstTypeRegistry.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstTypeRegistry.kt similarity index 96% rename from buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstTypeRegistry.kt rename to buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstTypeRegistry.kt index f776e6a..ea96776 100644 --- a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstTypeRegistry.kt +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstTypeRegistry.kt @@ -1,4 +1,4 @@ -package gay.pizza.pork.gradle.ast +package gay.pizza.pork.buildext.ast class AstTypeRegistry { private val internalTypes = mutableSetOf() diff --git a/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstTypeRole.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstTypeRole.kt new file mode 100644 index 0000000..d9f9165 --- /dev/null +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstTypeRole.kt @@ -0,0 +1,16 @@ +package gay.pizza.pork.buildext.ast + +enum class AstTypeRole { + RootNode, + HierarchyNode, + AstNode, + ValueHolder, + Enum; + + fun isNodeInherited(): Boolean = when (this) { + RootNode -> true + HierarchyNode -> true + AstNode -> true + else -> false + } +} diff --git a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstValue.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstValue.kt similarity index 73% rename from buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstValue.kt rename to buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstValue.kt index 7f1f034..31232fc 100644 --- a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstValue.kt +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstValue.kt @@ -1,4 +1,4 @@ -package gay.pizza.pork.gradle.ast +package gay.pizza.pork.buildext.ast class AstValue( val name: String, diff --git a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstValueDescription.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstValueDescription.kt similarity index 74% rename from buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstValueDescription.kt rename to buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstValueDescription.kt index dfd69cf..38dfaf5 100644 --- a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstValueDescription.kt +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstValueDescription.kt @@ -1,4 +1,4 @@ -package gay.pizza.pork.gradle.ast +package gay.pizza.pork.buildext.ast data class AstValueDescription( val name: String, diff --git a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstWorld.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstWorld.kt similarity index 68% rename from buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstWorld.kt rename to buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstWorld.kt index 5fb746e..c656506 100644 --- a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/AstWorld.kt +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstWorld.kt @@ -1,8 +1,25 @@ -package gay.pizza.pork.gradle.ast +package gay.pizza.pork.buildext.ast class AstWorld { val typeRegistry: AstTypeRegistry = AstTypeRegistry() + fun typesInDependencyOrder(): List { + val typesInNameOrder = typeRegistry.types.sortedBy { it.name } + val typesInDependencyOrder = mutableListOf() + for (type in typesInNameOrder) { + if (type.parent != null) { + if (!typesInDependencyOrder.contains(type.parent)) { + typesInDependencyOrder.add(type.parent!!) + } + } + + if (!typesInDependencyOrder.contains(type)) { + typesInDependencyOrder.add(type) + } + } + return typesInDependencyOrder + } + companion object { fun build(description: AstDescription): AstWorld { val world = AstWorld() diff --git a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/RunCodegenIde.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/RunCodegenIde.kt similarity index 58% rename from buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/RunCodegenIde.kt rename to buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/RunCodegenIde.kt index eca1f6d..9d23be0 100644 --- a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/ast/RunCodegenIde.kt +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/RunCodegenIde.kt @@ -1,4 +1,4 @@ -package gay.pizza.pork.gradle.ast +package gay.pizza.pork.buildext.ast import kotlin.io.path.Path @@ -6,9 +6,9 @@ object RunCodegenIde { @JvmStatic fun main(args: Array) { AstCodegen.run( - pkg = "gay.pizza.pork.gen", + pkg = "gay.pizza.pork.ast", astDescriptionFile = Path("src/main/ast/pork.yml"), - outputDirectory = Path("src/main/kotlin/gay/pizza/pork/gen") + outputDirectory = Path("src/main/kotlin/gay/pizza/pork/ast") ) } } diff --git a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/codegen/KotlinClass.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinClass.kt similarity index 76% rename from buildSrc/src/main/kotlin/gay/pizza/pork/gradle/codegen/KotlinClass.kt rename to buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinClass.kt index ce1f4e1..f4e3f3b 100644 --- a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/codegen/KotlinClass.kt +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinClass.kt @@ -1,12 +1,14 @@ -package gay.pizza.pork.gradle.codegen +package gay.pizza.pork.buildext.codegen class KotlinClass( override val pkg: String, override var name: String, var sealed: Boolean = false, - override var inherits: MutableList = mutableListOf(), + var isInterface: Boolean = false, override var imports: MutableList = mutableListOf(), - override var members: MutableList = mutableListOf(), override var annotations: MutableList = mutableListOf(), + override var typeParameters: MutableList = mutableListOf(), + override var inherits: MutableList = mutableListOf(), + override var members: MutableList = mutableListOf(), override var functions: MutableList = mutableListOf() ) : KotlinClassLike() diff --git a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/codegen/KotlinClassLike.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinClassLike.kt similarity index 78% rename from buildSrc/src/main/kotlin/gay/pizza/pork/gradle/codegen/KotlinClassLike.kt rename to buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinClassLike.kt index e88ef87..8d3db6b 100644 --- a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/codegen/KotlinClassLike.kt +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinClassLike.kt @@ -1,11 +1,12 @@ -package gay.pizza.pork.gradle.codegen +package gay.pizza.pork.buildext.codegen abstract class KotlinClassLike { abstract val pkg: String abstract val name: String abstract var imports: MutableList - abstract var inherits: MutableList abstract var annotations: MutableList + abstract var typeParameters: MutableList + abstract var inherits: MutableList abstract var members: MutableList abstract var functions: MutableList } diff --git a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/codegen/KotlinEnum.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinEnum.kt similarity index 71% rename from buildSrc/src/main/kotlin/gay/pizza/pork/gradle/codegen/KotlinEnum.kt rename to buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinEnum.kt index a2d48e2..1dbb243 100644 --- a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/codegen/KotlinEnum.kt +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinEnum.kt @@ -1,12 +1,13 @@ -package gay.pizza.pork.gradle.codegen +package gay.pizza.pork.buildext.codegen class KotlinEnum( override val pkg: String, override val name: String, override var imports: MutableList = mutableListOf(), - override var inherits: MutableList = mutableListOf(), override var annotations: MutableList = mutableListOf(), + override var typeParameters: MutableList = mutableListOf(), + override var inherits: MutableList = mutableListOf(), override var members: MutableList = mutableListOf(), override var functions: MutableList = mutableListOf(), - var entries: MutableList = mutableListOf(), + var entries: MutableList = mutableListOf() ) : KotlinClassLike() diff --git a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/codegen/KotlinEnumEntry.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinEnumEntry.kt similarity index 71% rename from buildSrc/src/main/kotlin/gay/pizza/pork/gradle/codegen/KotlinEnumEntry.kt rename to buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinEnumEntry.kt index 564c3ae..6367532 100644 --- a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/codegen/KotlinEnumEntry.kt +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinEnumEntry.kt @@ -1,4 +1,4 @@ -package gay.pizza.pork.gradle.codegen +package gay.pizza.pork.buildext.codegen class KotlinEnumEntry( val name: String, diff --git a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/codegen/KotlinFunction.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinFunction.kt similarity index 53% rename from buildSrc/src/main/kotlin/gay/pizza/pork/gradle/codegen/KotlinFunction.kt rename to buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinFunction.kt index 32163ba..62e4bdf 100644 --- a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/codegen/KotlinFunction.kt +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinFunction.kt @@ -1,11 +1,14 @@ -package gay.pizza.pork.gradle.codegen +package gay.pizza.pork.buildext.codegen class KotlinFunction( val name: String, + var typeParameters: MutableList = mutableListOf(), var parameters: MutableList = mutableListOf(), var returnType: String? = null, var abstract: Boolean = false, + var open: Boolean = false, var overridden: Boolean = false, var isImmediateExpression: Boolean = false, - var body: MutableList = mutableListOf() + var body: MutableList = mutableListOf(), + var isInterfaceMethod: Boolean = false ) diff --git a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/codegen/KotlinMember.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinMember.kt similarity index 56% rename from buildSrc/src/main/kotlin/gay/pizza/pork/gradle/codegen/KotlinMember.kt rename to buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinMember.kt index a996647..3fda86f 100644 --- a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/codegen/KotlinMember.kt +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinMember.kt @@ -1,9 +1,10 @@ -package gay.pizza.pork.gradle.codegen +package gay.pizza.pork.buildext.codegen class KotlinMember( var name: String, var type: String, var abstract: Boolean = false, var overridden: Boolean = false, - var value: String? = null + var value: String? = null, + var mutable: Boolean = false ) diff --git a/buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinParameter.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinParameter.kt new file mode 100644 index 0000000..9d01ee4 --- /dev/null +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinParameter.kt @@ -0,0 +1,8 @@ +package gay.pizza.pork.buildext.codegen + +class KotlinParameter( + val name: String, + var type: String, + var defaultValue: String? = null, + var vararg: Boolean = false +) diff --git a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/codegen/KotlinWriter.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinWriter.kt similarity index 69% rename from buildSrc/src/main/kotlin/gay/pizza/pork/gradle/codegen/KotlinWriter.kt rename to buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinWriter.kt index c89fca2..e445861 100644 --- a/buildSrc/src/main/kotlin/gay/pizza/pork/gradle/codegen/KotlinWriter.kt +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinWriter.kt @@ -1,14 +1,18 @@ -package gay.pizza.pork.gradle.codegen +package gay.pizza.pork.buildext.codegen -class KotlinWriter { +class KotlinWriter() { private val buffer = StringBuilder() - constructor(kotlinClassLike: KotlinClassLike) { + constructor(kotlinClassLike: KotlinClassLike) : this() { write(kotlinClassLike) } fun writeClass(kotlinClass: KotlinClass): Unit = buffer.run { - val classType = if (kotlinClass.sealed) "sealed class" else "class" + val classType = when { + kotlinClass.sealed -> "sealed class" + kotlinClass.isInterface -> "interface" + else -> "class" + } writeClassLike(classType, kotlinClass) val members = kotlinClass.members.filter { it.abstract || (it.overridden && it.value != null) @@ -20,13 +24,14 @@ class KotlinWriter { } for (member in members) { + val form = if (member.mutable) "var" else "val" if (member.abstract) { - appendLine(" abstract val ${member.name}: ${member.type}") + appendLine(" abstract $form ${member.name}: ${member.type}") } else { if (member.overridden) { append(" override ") } - append("val ${member.name}: ${member.type}") + append("$form ${member.name}: ${member.type}") if (member.value != null) { append(" = ") append(member.value) @@ -107,6 +112,10 @@ class KotlinWriter { } append("$classType ${kotlinClass.name}") + if (kotlinClass.typeParameters.isNotEmpty()) { + val typeParameters = kotlinClass.typeParameters.joinToString(", ") + append("<${typeParameters}>") + } val contructedMembers = kotlinClass.members.filter { !it.abstract && !(it.overridden && it.value != null) @@ -115,7 +124,8 @@ class KotlinWriter { if (contructedMembers.isNotEmpty()) { val constructor = contructedMembers.joinToString(", ") { val prefix = if (it.overridden) "override " else "" - val start = "${prefix}val ${it.name}: ${it.type}" + val form = if (it.mutable) "var" else "val" + val start = "${prefix}$form ${it.name}: ${it.type}" if (it.value != null) { "$start = ${it.value}" } else start @@ -129,7 +139,7 @@ class KotlinWriter { } private fun writeFunctions(kotlinClassLike: KotlinClassLike): Unit = buffer.run { - for (function in kotlinClassLike.functions) { + for ((index, function) in kotlinClassLike.functions.withIndex()) { append(" ") if (function.overridden) { @@ -140,9 +150,20 @@ class KotlinWriter { append("abstract ") } - append("fun ${function.name}(") + if (function.open) { + append("open ") + } + + append("fun ") + if (function.typeParameters.isNotEmpty()) { + append("<${function.typeParameters.joinToString(", ")}> ") + } + append("${function.name}(") append(function.parameters.joinToString(", ") { - val start = "${it.name}: ${it.type}" + var start = "${it.name}: ${it.type}" + if (it.vararg) { + start = "vararg $start" + } if (it.defaultValue != null) { start + " = ${it.defaultValue}" } else start @@ -152,10 +173,10 @@ class KotlinWriter { append(": ${function.returnType}") } - if (!function.isImmediateExpression) { + if (!function.isImmediateExpression && !function.abstract && !function.isInterfaceMethod) { append(" {") - } else { - appendLine(" =") + } else if (!function.abstract && !function.isInterfaceMethod) { + append(" =") } if (function.body.isNotEmpty()) { @@ -166,8 +187,19 @@ class KotlinWriter { } } - if (!function.isImmediateExpression) { - appendLine(" }") + 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() } } } diff --git a/common/build.gradle.kts b/common/build.gradle.kts index e842a6a..80b554b 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -1,3 +1,3 @@ plugins { - pork_module + id("gay.pizza.pork.module") } diff --git a/evaluator/build.gradle.kts b/evaluator/build.gradle.kts index 04ad213..9cf56b2 100644 --- a/evaluator/build.gradle.kts +++ b/evaluator/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - pork_module + id("gay.pizza.pork.module") } dependencies { diff --git a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationVisitor.kt b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationVisitor.kt index bf79447..5e05b07 100644 --- a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationVisitor.kt +++ b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationVisitor.kt @@ -101,7 +101,7 @@ class EvaluationVisitor(root: Scope) : NodeVisitor { } } - override fun visitFunctionDeclaration(node: FunctionDefinition): Any { + override fun visitFunctionDefinition(node: FunctionDefinition): Any { val blockFunction = visitBlock(node.block) as BlockFunction val function = CallableFunction { arguments -> currentScope = currentScope.fork() diff --git a/frontend/build.gradle.kts b/frontend/build.gradle.kts index 05f9399..bbb13ce 100644 --- a/frontend/build.gradle.kts +++ b/frontend/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - pork_module + id("gay.pizza.pork.module") } dependencies { diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..a7efeab --- /dev/null +++ b/gradle.properties @@ -0,0 +1 @@ +org.gradle.warning.mode=none diff --git a/parser/build.gradle.kts b/parser/build.gradle.kts index c5d32a0..4394faa 100644 --- a/parser/build.gradle.kts +++ b/parser/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - pork_module + id("gay.pizza.pork.module") } dependencies { diff --git a/parser/src/main/kotlin/gay/pizza/pork/parser/Printer.kt b/parser/src/main/kotlin/gay/pizza/pork/parser/Printer.kt index 93aebee..b6df1c5 100644 --- a/parser/src/main/kotlin/gay/pizza/pork/parser/Printer.kt +++ b/parser/src/main/kotlin/gay/pizza/pork/parser/Printer.kt @@ -147,7 +147,7 @@ class Printer(buffer: StringBuilder) : NodeVisitor { visit(node.right) } - override fun visitFunctionDeclaration(node: FunctionDefinition) { + override fun visitFunctionDefinition(node: FunctionDefinition) { append("fn ") visit(node.symbol) append("(") diff --git a/settings.gradle.kts b/settings.gradle.kts index 75d3ffd..138867e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,5 +1,7 @@ rootProject.name = "pork" +includeBuild("buildext") + include( ":common", ":ast", diff --git a/tool/build.gradle.kts b/tool/build.gradle.kts index 460fcc1..4c663db 100644 --- a/tool/build.gradle.kts +++ b/tool/build.gradle.kts @@ -1,6 +1,6 @@ plugins { application - pork_module + id("gay.pizza.pork.module") id("com.github.johnrengelman.shadow") version "8.1.1" id("org.graalvm.buildtools.native") version "0.9.25" } @@ -15,9 +15,19 @@ dependencies { } application { + applicationName = "pork" mainClass.set("gay.pizza.pork.tool.MainKt") } +for (task in arrayOf(tasks.shadowDistTar, tasks.shadowDistZip, tasks.shadowJar)) { + val suffix = when { + task == tasks.shadowJar -> "" + task.name.startsWith("shadow") -> "-shadow" + else -> "" + } + task.get().archiveBaseName.set("pork${suffix}") +} + graalvmNative { binaries { named("main") {