mirror of
https://github.com/GayPizzaSpecifications/pork.git
synced 2025-08-02 21:00:56 +00:00
Auto-generate the AST.
This commit is contained in:
parent
f06ea93dc4
commit
174d51ca1c
10
.github/workflows/build.yml
vendored
10
.github/workflows/build.yml
vendored
@ -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
|
||||
|
22
.github/workflows/graal.yml
vendored
Normal file
22
.github/workflows/graal.yml
vendored
Normal file
@ -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
|
@ -1,5 +1,4 @@
|
||||
plugins {
|
||||
pork_module
|
||||
|
||||
id("gay.pizza.pork.module")
|
||||
id("gay.pizza.pork.ast")
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ types:
|
||||
- name: declarations
|
||||
type: List<Declaration>
|
||||
- name: definitions
|
||||
type: List<Declaration>
|
||||
type: List<Definition>
|
||||
LetAssignment:
|
||||
parent: Expression
|
||||
values:
|
||||
|
@ -9,15 +9,16 @@ class CompilationUnit(val declarations: List<Declaration>, val definitions: List
|
||||
override val type: NodeType = NodeType.CompilationUnit
|
||||
|
||||
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||
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
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@SerialName("declaration")
|
||||
sealed class Declaration : Node()
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -1,6 +1,8 @@
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@SerialName("expression")
|
||||
sealed class Expression : Node()
|
||||
|
@ -5,24 +5,20 @@ import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@SerialName("functionDefinition")
|
||||
class FunctionDefinition(
|
||||
override val modifiers: DefinitionModifiers,
|
||||
override val symbol: Symbol,
|
||||
val arguments: List<Symbol>,
|
||||
val block: Block
|
||||
) : Definition() {
|
||||
class FunctionDefinition(override val modifiers: DefinitionModifiers, override val symbol: Symbol, val arguments: List<Symbol>, val block: Block) : Definition() {
|
||||
override val type: NodeType = NodeType.FunctionDefinition
|
||||
|
||||
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||
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()
|
||||
|
@ -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 <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||
@ -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
|
||||
}
|
||||
|
@ -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 <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 <T> visitChildren(visitor: NodeVisitor<T>): List<T> = emptyList()
|
||||
|
||||
open fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||
emptyList()
|
||||
}
|
||||
|
@ -1,26 +1,58 @@
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
class NodeCoalescer(val handler: (Node) -> Unit) : NodeVisitor<Unit> {
|
||||
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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -1,60 +1,64 @@
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
interface NodeVisitor<T> {
|
||||
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<T> =
|
||||
nodes.asSequence().filterNotNull().map { visit(it) }.toList()
|
||||
|
||||
fun visitAll(vararg nodeLists: List<Node>): List<T> =
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,8 @@
|
||||
plugins {
|
||||
kotlin("jvm") version "1.9.10" apply false
|
||||
kotlin("plugin.serialization") version "1.9.10" apply false
|
||||
}
|
||||
|
||||
tasks.withType<Wrapper> {
|
||||
gradleVersion = "8.3"
|
||||
}
|
||||
|
@ -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<AstType>()
|
||||
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<String>()
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
package gay.pizza.pork.gradle.ast
|
||||
|
||||
enum class AstTypeRole {
|
||||
RootNode,
|
||||
HierarchyNode,
|
||||
AstNode,
|
||||
ValueHolder,
|
||||
Enum
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package gay.pizza.pork.gradle.codegen
|
||||
|
||||
class KotlinParameter(
|
||||
val name: String,
|
||||
val type: String,
|
||||
val defaultValue: String? = 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<KotlinCompile> {
|
||||
kotlinOptions.jvmTarget = "17"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.jetbrains.kotlin:kotlin-bom")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
@ -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() {
|
@ -1,4 +1,4 @@
|
||||
package gay.pizza.pork.gradle
|
||||
package gay.pizza.pork.buildext
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
@ -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<Project> {
|
||||
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<JavaPluginExtension>().apply {
|
||||
val javaVersion = JavaVersion.toVersion(17)
|
||||
sourceCompatibility = javaVersion
|
||||
targetCompatibility = javaVersion
|
||||
}
|
||||
|
||||
target.tasks.withType<KotlinCompile> {
|
||||
kotlinOptions.jvmTarget = "17"
|
||||
}
|
||||
|
||||
target.dependencies {
|
||||
add("implementation", "org.jetbrains.kotlin:kotlin-bom")
|
||||
add("implementation", "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
|
||||
}
|
||||
}
|
||||
}
|
@ -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<T>",
|
||||
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<T>",
|
||||
parameters = mutableListOf(
|
||||
KotlinParameter("nodeLists", type = "List<Node>", 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<Unit>"),
|
||||
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<T>",
|
||||
open = true,
|
||||
typeParameters = mutableListOf("T"),
|
||||
parameters = mutableListOf(
|
||||
KotlinParameter("visitor", "NodeVisitor<T>")
|
||||
),
|
||||
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<String>()
|
||||
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<T>",
|
||||
typeParameters = mutableListOf("T")
|
||||
)
|
||||
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()) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package gay.pizza.pork.gradle.ast
|
||||
package gay.pizza.pork.buildext.ast
|
||||
|
||||
data class AstDescription(
|
||||
val root: String,
|
@ -1,4 +1,4 @@
|
||||
package gay.pizza.pork.gradle.ast
|
||||
package gay.pizza.pork.buildext.ast
|
||||
|
||||
class AstEnum(
|
||||
val name: String,
|
@ -1,4 +1,4 @@
|
||||
package gay.pizza.pork.gradle.ast
|
||||
package gay.pizza.pork.buildext.ast
|
||||
|
||||
class AstEnumDescription(
|
||||
val name: String,
|
@ -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"),
|
@ -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<AstValue>()
|
@ -1,4 +1,4 @@
|
||||
package gay.pizza.pork.gradle.ast
|
||||
package gay.pizza.pork.buildext.ast
|
||||
|
||||
data class AstTypeDescription(
|
||||
val parent: String? = null,
|
@ -1,4 +1,4 @@
|
||||
package gay.pizza.pork.gradle.ast
|
||||
package gay.pizza.pork.buildext.ast
|
||||
|
||||
class AstTypeRef(
|
||||
val type: AstType? = null,
|
@ -1,4 +1,4 @@
|
||||
package gay.pizza.pork.gradle.ast
|
||||
package gay.pizza.pork.buildext.ast
|
||||
|
||||
enum class AstTypeRefForm {
|
||||
Single,
|
@ -1,4 +1,4 @@
|
||||
package gay.pizza.pork.gradle.ast
|
||||
package gay.pizza.pork.buildext.ast
|
||||
|
||||
class AstTypeRegistry {
|
||||
private val internalTypes = mutableSetOf<AstType>()
|
@ -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
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package gay.pizza.pork.gradle.ast
|
||||
package gay.pizza.pork.buildext.ast
|
||||
|
||||
class AstValue(
|
||||
val name: String,
|
@ -1,4 +1,4 @@
|
||||
package gay.pizza.pork.gradle.ast
|
||||
package gay.pizza.pork.buildext.ast
|
||||
|
||||
data class AstValueDescription(
|
||||
val name: String,
|
@ -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<AstType> {
|
||||
val typesInNameOrder = typeRegistry.types.sortedBy { it.name }
|
||||
val typesInDependencyOrder = mutableListOf<AstType>()
|
||||
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()
|
@ -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<String>) {
|
||||
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")
|
||||
)
|
||||
}
|
||||
}
|
@ -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<String> = mutableListOf(),
|
||||
var isInterface: Boolean = false,
|
||||
override var imports: MutableList<String> = mutableListOf(),
|
||||
override var members: MutableList<KotlinMember> = mutableListOf(),
|
||||
override var annotations: MutableList<String> = mutableListOf(),
|
||||
override var typeParameters: MutableList<String> = mutableListOf(),
|
||||
override var inherits: MutableList<String> = mutableListOf(),
|
||||
override var members: MutableList<KotlinMember> = mutableListOf(),
|
||||
override var functions: MutableList<KotlinFunction> = mutableListOf()
|
||||
) : KotlinClassLike()
|
@ -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<String>
|
||||
abstract var inherits: MutableList<String>
|
||||
abstract var annotations: MutableList<String>
|
||||
abstract var typeParameters: MutableList<String>
|
||||
abstract var inherits: MutableList<String>
|
||||
abstract var members: MutableList<KotlinMember>
|
||||
abstract var functions: MutableList<KotlinFunction>
|
||||
}
|
@ -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<String> = mutableListOf(),
|
||||
override var inherits: MutableList<String> = mutableListOf(),
|
||||
override var annotations: MutableList<String> = mutableListOf(),
|
||||
override var typeParameters: MutableList<String> = mutableListOf(),
|
||||
override var inherits: MutableList<String> = mutableListOf(),
|
||||
override var members: MutableList<KotlinMember> = mutableListOf(),
|
||||
override var functions: MutableList<KotlinFunction> = mutableListOf(),
|
||||
var entries: MutableList<KotlinEnumEntry> = mutableListOf(),
|
||||
var entries: MutableList<KotlinEnumEntry> = mutableListOf()
|
||||
) : KotlinClassLike()
|
@ -1,4 +1,4 @@
|
||||
package gay.pizza.pork.gradle.codegen
|
||||
package gay.pizza.pork.buildext.codegen
|
||||
|
||||
class KotlinEnumEntry(
|
||||
val name: String,
|
@ -1,11 +1,14 @@
|
||||
package gay.pizza.pork.gradle.codegen
|
||||
package gay.pizza.pork.buildext.codegen
|
||||
|
||||
class KotlinFunction(
|
||||
val name: String,
|
||||
var typeParameters: MutableList<String> = mutableListOf(),
|
||||
var parameters: MutableList<KotlinParameter> = mutableListOf(),
|
||||
var returnType: String? = null,
|
||||
var abstract: Boolean = false,
|
||||
var open: Boolean = false,
|
||||
var overridden: Boolean = false,
|
||||
var isImmediateExpression: Boolean = false,
|
||||
var body: MutableList<String> = mutableListOf()
|
||||
var body: MutableList<String> = mutableListOf(),
|
||||
var isInterfaceMethod: Boolean = false
|
||||
)
|
@ -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
|
||||
)
|
@ -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
|
||||
)
|
@ -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,9 +187,20 @@ class KotlinWriter {
|
||||
}
|
||||
}
|
||||
|
||||
if (!function.isImmediateExpression) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,3 @@
|
||||
plugins {
|
||||
pork_module
|
||||
id("gay.pizza.pork.module")
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
pork_module
|
||||
id("gay.pizza.pork.module")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@ -101,7 +101,7 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
|
@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
pork_module
|
||||
id("gay.pizza.pork.module")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
1
gradle.properties
Normal file
1
gradle.properties
Normal file
@ -0,0 +1 @@
|
||||
org.gradle.warning.mode=none
|
@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
pork_module
|
||||
id("gay.pizza.pork.module")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@ -147,7 +147,7 @@ class Printer(buffer: StringBuilder) : NodeVisitor<Unit> {
|
||||
visit(node.right)
|
||||
}
|
||||
|
||||
override fun visitFunctionDeclaration(node: FunctionDefinition) {
|
||||
override fun visitFunctionDefinition(node: FunctionDefinition) {
|
||||
append("fn ")
|
||||
visit(node.symbol)
|
||||
append("(")
|
||||
|
@ -1,5 +1,7 @@
|
||||
rootProject.name = "pork"
|
||||
|
||||
includeBuild("buildext")
|
||||
|
||||
include(
|
||||
":common",
|
||||
":ast",
|
||||
|
@ -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") {
|
||||
|
Loading…
Reference in New Issue
Block a user