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
|
uses: gradle/gradle-build-action@v2
|
||||||
with:
|
with:
|
||||||
arguments: build
|
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 {
|
plugins {
|
||||||
pork_module
|
id("gay.pizza.pork.module")
|
||||||
|
|
||||||
id("gay.pizza.pork.ast")
|
id("gay.pizza.pork.ast")
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ types:
|
|||||||
- name: declarations
|
- name: declarations
|
||||||
type: List<Declaration>
|
type: List<Declaration>
|
||||||
- name: definitions
|
- name: definitions
|
||||||
type: List<Declaration>
|
type: List<Definition>
|
||||||
LetAssignment:
|
LetAssignment:
|
||||||
parent: Expression
|
parent: Expression
|
||||||
values:
|
values:
|
||||||
|
@ -9,15 +9,16 @@ class CompilationUnit(val declarations: List<Declaration>, val definitions: List
|
|||||||
override val type: NodeType = NodeType.CompilationUnit
|
override val type: NodeType = NodeType.CompilationUnit
|
||||||
|
|
||||||
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||||
visitor.visitAll(declarations)
|
visitor.visitAll(declarations, definitions)
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (other !is CompilationUnit) return false
|
if (other !is CompilationUnit) return false
|
||||||
return other.declarations == declarations
|
return other.declarations == declarations && other.definitions == definitions
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
var result = declarations.hashCode()
|
var result = declarations.hashCode()
|
||||||
|
result = 31 * result + definitions.hashCode()
|
||||||
result = 31 * result + type.hashCode()
|
result = 31 * result + type.hashCode()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package gay.pizza.pork.ast
|
package gay.pizza.pork.ast
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
@SerialName("declaration")
|
||||||
sealed class Declaration : Node()
|
sealed class Declaration : Node()
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
package gay.pizza.pork.ast
|
package gay.pizza.pork.ast
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
@SerialName("definition")
|
||||||
sealed class Definition : Node() {
|
sealed class Definition : Node() {
|
||||||
abstract val symbol: Symbol
|
abstract val symbol: Symbol
|
||||||
abstract val modifiers: DefinitionModifiers
|
abstract val modifiers: DefinitionModifiers
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package gay.pizza.pork.ast
|
package gay.pizza.pork.ast
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class DefinitionModifiers(
|
@SerialName("definitionModifiers")
|
||||||
var export: Boolean
|
class DefinitionModifiers(var export: Boolean)
|
||||||
)
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package gay.pizza.pork.ast
|
package gay.pizza.pork.ast
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
@SerialName("expression")
|
||||||
sealed class Expression : Node()
|
sealed class Expression : Node()
|
||||||
|
@ -5,24 +5,20 @@ import kotlinx.serialization.Serializable
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("functionDefinition")
|
@SerialName("functionDefinition")
|
||||||
class FunctionDefinition(
|
class FunctionDefinition(override val modifiers: DefinitionModifiers, override val symbol: Symbol, val arguments: List<Symbol>, val block: Block) : Definition() {
|
||||||
override val modifiers: DefinitionModifiers,
|
|
||||||
override val symbol: Symbol,
|
|
||||||
val arguments: List<Symbol>,
|
|
||||||
val block: Block
|
|
||||||
) : Definition() {
|
|
||||||
override val type: NodeType = NodeType.FunctionDefinition
|
override val type: NodeType = NodeType.FunctionDefinition
|
||||||
|
|
||||||
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
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 {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (other !is FunctionDefinition) return false
|
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 {
|
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 + arguments.hashCode()
|
||||||
result = 31 * result + block.hashCode()
|
result = 31 * result + block.hashCode()
|
||||||
result = 31 * result + type.hashCode()
|
result = 31 * result + type.hashCode()
|
||||||
|
@ -5,11 +5,7 @@ import kotlinx.serialization.Serializable
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("if")
|
@SerialName("if")
|
||||||
class If(
|
class If(val condition: Expression, val thenExpression: Expression, val elseExpression: Expression?) : Expression() {
|
||||||
val condition: Expression,
|
|
||||||
val thenExpression: Expression,
|
|
||||||
val elseExpression: Expression? = null
|
|
||||||
) : Expression() {
|
|
||||||
override val type: NodeType = NodeType.If
|
override val type: NodeType = NodeType.If
|
||||||
|
|
||||||
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||||
@ -17,15 +13,13 @@ class If(
|
|||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (other !is If) return false
|
if (other !is If) return false
|
||||||
return other.condition == condition &&
|
return other.condition == condition && other.thenExpression == thenExpression && other.elseExpression == elseExpression
|
||||||
other.thenExpression == thenExpression &&
|
|
||||||
other.elseExpression == elseExpression
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
var result = condition.hashCode()
|
var result = condition.hashCode()
|
||||||
result = 31 * result + thenExpression.hashCode()
|
result = 31 * result + thenExpression.hashCode()
|
||||||
result = 31 * result + (elseExpression?.hashCode() ?: 0)
|
result = 31 * result + elseExpression.hashCode()
|
||||||
result = 31 * result + type.hashCode()
|
result = 31 * result + type.hashCode()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,7 @@ import kotlinx.serialization.Serializable
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("infixOperation")
|
@SerialName("infixOperation")
|
||||||
class InfixOperation(
|
class InfixOperation(val left: Expression, val op: InfixOperator, val right: Expression) : Expression() {
|
||||||
val left: Expression,
|
|
||||||
val op: InfixOperator,
|
|
||||||
val right: Expression
|
|
||||||
) : Expression() {
|
|
||||||
override val type: NodeType = NodeType.InfixOperation
|
override val type: NodeType = NodeType.InfixOperation
|
||||||
|
|
||||||
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||||
@ -17,9 +13,7 @@ class InfixOperation(
|
|||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (other !is InfixOperation) return false
|
if (other !is InfixOperation) return false
|
||||||
return other.op == op &&
|
return other.left == left && other.op == op && other.right == right
|
||||||
other.left == left &&
|
|
||||||
other.right == right
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
|
@ -14,7 +14,7 @@ class IntLiteral(val value: Int) : Expression() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
var result = value
|
var result = value.hashCode()
|
||||||
result = 31 * result + type.hashCode()
|
result = 31 * result + type.hashCode()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
package gay.pizza.pork.ast
|
package gay.pizza.pork.ast
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
@SerialName("node")
|
||||||
sealed class Node {
|
sealed class Node {
|
||||||
abstract val type: NodeType
|
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
|
package gay.pizza.pork.ast
|
||||||
|
|
||||||
class NodeCoalescer(val handler: (Node) -> Unit) : NodeVisitor<Unit> {
|
class NodeCoalescer(val handler: (Node) -> Unit) : NodeVisitor<Unit> {
|
||||||
override fun visitIntLiteral(node: IntLiteral): Unit = handle(node)
|
override fun visitBlock(node: Block): Unit =
|
||||||
override fun visitStringLiteral(node: StringLiteral): Unit = handle(node)
|
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 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)
|
handler(node)
|
||||||
node.visitChildren(this)
|
node.visitChildren(this)
|
||||||
}
|
}
|
||||||
|
@ -2,24 +2,24 @@ package gay.pizza.pork.ast
|
|||||||
|
|
||||||
enum class NodeType(val parent: NodeType? = null) {
|
enum class NodeType(val parent: NodeType? = null) {
|
||||||
Node,
|
Node,
|
||||||
Symbol(Node),
|
Block(Node),
|
||||||
Expression(Node),
|
Expression(Node),
|
||||||
|
BooleanLiteral(Expression),
|
||||||
|
CompilationUnit(Node),
|
||||||
Declaration(Node),
|
Declaration(Node),
|
||||||
Definition(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),
|
FunctionCall(Expression),
|
||||||
|
FunctionDefinition(Definition),
|
||||||
If(Expression),
|
If(Expression),
|
||||||
ImportDeclaration(Declaration),
|
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
|
package gay.pizza.pork.ast
|
||||||
|
|
||||||
interface NodeVisitor<T> {
|
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 visitBlock(node: Block): T
|
||||||
|
|
||||||
fun visitImportDeclaration(node: ImportDeclaration): T
|
fun visitBooleanLiteral(node: BooleanLiteral): T
|
||||||
|
|
||||||
fun visitCompilationUnit(node: CompilationUnit): T
|
fun visitCompilationUnit(node: CompilationUnit): T
|
||||||
|
|
||||||
fun visitExpression(node: Expression): T = when (node) {
|
fun visitFunctionCall(node: FunctionCall): T
|
||||||
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 visitDeclaration(node: Declaration): T = when (node) {
|
fun visitFunctionDefinition(node: FunctionDefinition): T
|
||||||
is ImportDeclaration -> visitImportDeclaration(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visitDefinition(node: Definition): T = when (node) {
|
fun visitIf(node: If): T
|
||||||
is FunctionDefinition -> visitFunctionDeclaration(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun visit(node: Node): T = when (node) {
|
fun visitImportDeclaration(node: ImportDeclaration): T
|
||||||
is Symbol -> visitSymbol(node)
|
|
||||||
is Expression -> visitExpression(node)
|
fun visitInfixOperation(node: InfixOperation): T
|
||||||
is CompilationUnit -> visitCompilationUnit(node)
|
|
||||||
is Block -> visitBlock(node)
|
fun visitIntLiteral(node: IntLiteral): T
|
||||||
is Declaration -> visitDeclaration(node)
|
|
||||||
is Definition -> visitDefinition(node)
|
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> =
|
fun visitNodes(vararg nodes: Node?): List<T> =
|
||||||
nodes.asSequence().filterNotNull().map { visit(it) }.toList()
|
nodes.asSequence().filterNotNull().map { visit(it) }.toList()
|
||||||
|
|
||||||
fun visitAll(vararg nodeLists: List<Node>): List<T> =
|
fun visitAll(vararg nodeLists: List<Node>): List<T> =
|
||||||
nodeLists.asSequence().flatten().map { visit(it) }.toList()
|
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> {
|
tasks.withType<Wrapper> {
|
||||||
gradleVersion = "8.3"
|
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 {
|
plugins {
|
||||||
create("pork_ast") {
|
create("pork_ast") {
|
||||||
id = "gay.pizza.pork.ast"
|
id = "gay.pizza.pork.ast"
|
||||||
implementationClass = "gay.pizza.pork.gradle.PorkAstPlugin"
|
implementationClass = "gay.pizza.pork.buildext.PorkAstPlugin"
|
||||||
|
|
||||||
displayName = "Pork AST"
|
displayName = "Pork AST"
|
||||||
description = "AST generation code for pork"
|
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.DefaultTask
|
||||||
import org.gradle.api.tasks.Input
|
import org.gradle.api.tasks.Input
|
||||||
import org.gradle.api.tasks.InputFile
|
import org.gradle.api.tasks.InputFile
|
||||||
@ -17,10 +17,10 @@ open class GenerateAstCode : DefaultTask() {
|
|||||||
var astDescriptionFile: File = project.file("src/main/ast/pork.yml")
|
var astDescriptionFile: File = project.file("src/main/ast/pork.yml")
|
||||||
|
|
||||||
@get:Input
|
@get:Input
|
||||||
var codePackage: String = "gay.pizza.pork.gen"
|
var codePackage: String = "gay.pizza.pork.ast"
|
||||||
|
|
||||||
@get:OutputDirectory
|
@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
|
@TaskAction
|
||||||
fun generate() {
|
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.Plugin
|
||||||
import org.gradle.api.Project
|
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(
|
data class AstDescription(
|
||||||
val root: String,
|
val root: String,
|
@ -1,4 +1,4 @@
|
|||||||
package gay.pizza.pork.gradle.ast
|
package gay.pizza.pork.buildext.ast
|
||||||
|
|
||||||
class AstEnum(
|
class AstEnum(
|
||||||
val name: String,
|
val name: String,
|
@ -1,4 +1,4 @@
|
|||||||
package gay.pizza.pork.gradle.ast
|
package gay.pizza.pork.buildext.ast
|
||||||
|
|
||||||
class AstEnumDescription(
|
class AstEnumDescription(
|
||||||
val name: String,
|
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) {
|
enum class AstPrimitive(val id: kotlin.String) {
|
||||||
Boolean("Boolean"),
|
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) {
|
class AstType(val name: String, var parent: AstType? = null) {
|
||||||
private val internalValues = mutableListOf<AstValue>()
|
private val internalValues = mutableListOf<AstValue>()
|
@ -1,4 +1,4 @@
|
|||||||
package gay.pizza.pork.gradle.ast
|
package gay.pizza.pork.buildext.ast
|
||||||
|
|
||||||
data class AstTypeDescription(
|
data class AstTypeDescription(
|
||||||
val parent: String? = null,
|
val parent: String? = null,
|
@ -1,4 +1,4 @@
|
|||||||
package gay.pizza.pork.gradle.ast
|
package gay.pizza.pork.buildext.ast
|
||||||
|
|
||||||
class AstTypeRef(
|
class AstTypeRef(
|
||||||
val type: AstType? = null,
|
val type: AstType? = null,
|
@ -1,4 +1,4 @@
|
|||||||
package gay.pizza.pork.gradle.ast
|
package gay.pizza.pork.buildext.ast
|
||||||
|
|
||||||
enum class AstTypeRefForm {
|
enum class AstTypeRefForm {
|
||||||
Single,
|
Single,
|
@ -1,4 +1,4 @@
|
|||||||
package gay.pizza.pork.gradle.ast
|
package gay.pizza.pork.buildext.ast
|
||||||
|
|
||||||
class AstTypeRegistry {
|
class AstTypeRegistry {
|
||||||
private val internalTypes = mutableSetOf<AstType>()
|
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(
|
class AstValue(
|
||||||
val name: String,
|
val name: String,
|
@ -1,4 +1,4 @@
|
|||||||
package gay.pizza.pork.gradle.ast
|
package gay.pizza.pork.buildext.ast
|
||||||
|
|
||||||
data class AstValueDescription(
|
data class AstValueDescription(
|
||||||
val name: String,
|
val name: String,
|
@ -1,8 +1,25 @@
|
|||||||
package gay.pizza.pork.gradle.ast
|
package gay.pizza.pork.buildext.ast
|
||||||
|
|
||||||
class AstWorld {
|
class AstWorld {
|
||||||
val typeRegistry: AstTypeRegistry = AstTypeRegistry()
|
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 {
|
companion object {
|
||||||
fun build(description: AstDescription): AstWorld {
|
fun build(description: AstDescription): AstWorld {
|
||||||
val world = 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
|
import kotlin.io.path.Path
|
||||||
|
|
||||||
@ -6,9 +6,9 @@ object RunCodegenIde {
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
AstCodegen.run(
|
AstCodegen.run(
|
||||||
pkg = "gay.pizza.pork.gen",
|
pkg = "gay.pizza.pork.ast",
|
||||||
astDescriptionFile = Path("src/main/ast/pork.yml"),
|
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(
|
class KotlinClass(
|
||||||
override val pkg: String,
|
override val pkg: String,
|
||||||
override var name: String,
|
override var name: String,
|
||||||
var sealed: Boolean = false,
|
var sealed: Boolean = false,
|
||||||
override var inherits: MutableList<String> = mutableListOf(),
|
var isInterface: Boolean = false,
|
||||||
override var imports: MutableList<String> = mutableListOf(),
|
override var imports: MutableList<String> = mutableListOf(),
|
||||||
override var members: MutableList<KotlinMember> = mutableListOf(),
|
|
||||||
override var annotations: 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()
|
override var functions: MutableList<KotlinFunction> = mutableListOf()
|
||||||
) : KotlinClassLike()
|
) : KotlinClassLike()
|
@ -1,11 +1,12 @@
|
|||||||
package gay.pizza.pork.gradle.codegen
|
package gay.pizza.pork.buildext.codegen
|
||||||
|
|
||||||
abstract class KotlinClassLike {
|
abstract class KotlinClassLike {
|
||||||
abstract val pkg: String
|
abstract val pkg: String
|
||||||
abstract val name: String
|
abstract val name: String
|
||||||
abstract var imports: MutableList<String>
|
abstract var imports: MutableList<String>
|
||||||
abstract var inherits: MutableList<String>
|
|
||||||
abstract var annotations: MutableList<String>
|
abstract var annotations: MutableList<String>
|
||||||
|
abstract var typeParameters: MutableList<String>
|
||||||
|
abstract var inherits: MutableList<String>
|
||||||
abstract var members: MutableList<KotlinMember>
|
abstract var members: MutableList<KotlinMember>
|
||||||
abstract var functions: MutableList<KotlinFunction>
|
abstract var functions: MutableList<KotlinFunction>
|
||||||
}
|
}
|
@ -1,12 +1,13 @@
|
|||||||
package gay.pizza.pork.gradle.codegen
|
package gay.pizza.pork.buildext.codegen
|
||||||
|
|
||||||
class KotlinEnum(
|
class KotlinEnum(
|
||||||
override val pkg: String,
|
override val pkg: String,
|
||||||
override val name: String,
|
override val name: String,
|
||||||
override var imports: MutableList<String> = mutableListOf(),
|
override var imports: MutableList<String> = mutableListOf(),
|
||||||
override var inherits: MutableList<String> = mutableListOf(),
|
|
||||||
override var annotations: 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 members: MutableList<KotlinMember> = mutableListOf(),
|
||||||
override var functions: MutableList<KotlinFunction> = mutableListOf(),
|
override var functions: MutableList<KotlinFunction> = mutableListOf(),
|
||||||
var entries: MutableList<KotlinEnumEntry> = mutableListOf(),
|
var entries: MutableList<KotlinEnumEntry> = mutableListOf()
|
||||||
) : KotlinClassLike()
|
) : KotlinClassLike()
|
@ -1,4 +1,4 @@
|
|||||||
package gay.pizza.pork.gradle.codegen
|
package gay.pizza.pork.buildext.codegen
|
||||||
|
|
||||||
class KotlinEnumEntry(
|
class KotlinEnumEntry(
|
||||||
val name: String,
|
val name: String,
|
@ -1,11 +1,14 @@
|
|||||||
package gay.pizza.pork.gradle.codegen
|
package gay.pizza.pork.buildext.codegen
|
||||||
|
|
||||||
class KotlinFunction(
|
class KotlinFunction(
|
||||||
val name: String,
|
val name: String,
|
||||||
|
var typeParameters: MutableList<String> = mutableListOf(),
|
||||||
var parameters: MutableList<KotlinParameter> = mutableListOf(),
|
var parameters: MutableList<KotlinParameter> = mutableListOf(),
|
||||||
var returnType: String? = null,
|
var returnType: String? = null,
|
||||||
var abstract: Boolean = false,
|
var abstract: Boolean = false,
|
||||||
|
var open: Boolean = false,
|
||||||
var overridden: Boolean = false,
|
var overridden: Boolean = false,
|
||||||
var isImmediateExpression: 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(
|
class KotlinMember(
|
||||||
var name: String,
|
var name: String,
|
||||||
var type: String,
|
var type: String,
|
||||||
var abstract: Boolean = false,
|
var abstract: Boolean = false,
|
||||||
var overridden: 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()
|
private val buffer = StringBuilder()
|
||||||
|
|
||||||
constructor(kotlinClassLike: KotlinClassLike) {
|
constructor(kotlinClassLike: KotlinClassLike) : this() {
|
||||||
write(kotlinClassLike)
|
write(kotlinClassLike)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun writeClass(kotlinClass: KotlinClass): Unit = buffer.run {
|
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)
|
writeClassLike(classType, kotlinClass)
|
||||||
val members = kotlinClass.members.filter {
|
val members = kotlinClass.members.filter {
|
||||||
it.abstract || (it.overridden && it.value != null)
|
it.abstract || (it.overridden && it.value != null)
|
||||||
@ -20,13 +24,14 @@ class KotlinWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (member in members) {
|
for (member in members) {
|
||||||
|
val form = if (member.mutable) "var" else "val"
|
||||||
if (member.abstract) {
|
if (member.abstract) {
|
||||||
appendLine(" abstract val ${member.name}: ${member.type}")
|
appendLine(" abstract $form ${member.name}: ${member.type}")
|
||||||
} else {
|
} else {
|
||||||
if (member.overridden) {
|
if (member.overridden) {
|
||||||
append(" override ")
|
append(" override ")
|
||||||
}
|
}
|
||||||
append("val ${member.name}: ${member.type}")
|
append("$form ${member.name}: ${member.type}")
|
||||||
if (member.value != null) {
|
if (member.value != null) {
|
||||||
append(" = ")
|
append(" = ")
|
||||||
append(member.value)
|
append(member.value)
|
||||||
@ -107,6 +112,10 @@ class KotlinWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
append("$classType ${kotlinClass.name}")
|
append("$classType ${kotlinClass.name}")
|
||||||
|
if (kotlinClass.typeParameters.isNotEmpty()) {
|
||||||
|
val typeParameters = kotlinClass.typeParameters.joinToString(", ")
|
||||||
|
append("<${typeParameters}>")
|
||||||
|
}
|
||||||
|
|
||||||
val contructedMembers = kotlinClass.members.filter {
|
val contructedMembers = kotlinClass.members.filter {
|
||||||
!it.abstract && !(it.overridden && it.value != null)
|
!it.abstract && !(it.overridden && it.value != null)
|
||||||
@ -115,7 +124,8 @@ class KotlinWriter {
|
|||||||
if (contructedMembers.isNotEmpty()) {
|
if (contructedMembers.isNotEmpty()) {
|
||||||
val constructor = contructedMembers.joinToString(", ") {
|
val constructor = contructedMembers.joinToString(", ") {
|
||||||
val prefix = if (it.overridden) "override " else ""
|
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) {
|
if (it.value != null) {
|
||||||
"$start = ${it.value}"
|
"$start = ${it.value}"
|
||||||
} else start
|
} else start
|
||||||
@ -129,7 +139,7 @@ class KotlinWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun writeFunctions(kotlinClassLike: KotlinClassLike): Unit = buffer.run {
|
private fun writeFunctions(kotlinClassLike: KotlinClassLike): Unit = buffer.run {
|
||||||
for (function in kotlinClassLike.functions) {
|
for ((index, function) in kotlinClassLike.functions.withIndex()) {
|
||||||
append(" ")
|
append(" ")
|
||||||
|
|
||||||
if (function.overridden) {
|
if (function.overridden) {
|
||||||
@ -140,9 +150,20 @@ class KotlinWriter {
|
|||||||
append("abstract ")
|
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(", ") {
|
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) {
|
if (it.defaultValue != null) {
|
||||||
start + " = ${it.defaultValue}"
|
start + " = ${it.defaultValue}"
|
||||||
} else start
|
} else start
|
||||||
@ -152,10 +173,10 @@ class KotlinWriter {
|
|||||||
append(": ${function.returnType}")
|
append(": ${function.returnType}")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!function.isImmediateExpression) {
|
if (!function.isImmediateExpression && !function.abstract && !function.isInterfaceMethod) {
|
||||||
append(" {")
|
append(" {")
|
||||||
} else {
|
} else if (!function.abstract && !function.isInterfaceMethod) {
|
||||||
appendLine(" =")
|
append(" =")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (function.body.isNotEmpty()) {
|
if (function.body.isNotEmpty()) {
|
||||||
@ -166,8 +187,19 @@ class KotlinWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!function.isImmediateExpression) {
|
if (!function.isImmediateExpression && !function.abstract && !function.isInterfaceMethod) {
|
||||||
appendLine(" }")
|
if (function.body.isNotEmpty()) {
|
||||||
|
append(" ")
|
||||||
|
}
|
||||||
|
appendLine("}")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (function.abstract || function.isInterfaceMethod) {
|
||||||
|
appendLine()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index < kotlinClassLike.functions.size - 1) {
|
||||||
|
appendLine()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,3 +1,3 @@
|
|||||||
plugins {
|
plugins {
|
||||||
pork_module
|
id("gay.pizza.pork.module")
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
pork_module
|
id("gay.pizza.pork.module")
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
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 blockFunction = visitBlock(node.block) as BlockFunction
|
||||||
val function = CallableFunction { arguments ->
|
val function = CallableFunction { arguments ->
|
||||||
currentScope = currentScope.fork()
|
currentScope = currentScope.fork()
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
pork_module
|
id("gay.pizza.pork.module")
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
1
gradle.properties
Normal file
1
gradle.properties
Normal file
@ -0,0 +1 @@
|
|||||||
|
org.gradle.warning.mode=none
|
@ -1,5 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
pork_module
|
id("gay.pizza.pork.module")
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
@ -147,7 +147,7 @@ class Printer(buffer: StringBuilder) : NodeVisitor<Unit> {
|
|||||||
visit(node.right)
|
visit(node.right)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitFunctionDeclaration(node: FunctionDefinition) {
|
override fun visitFunctionDefinition(node: FunctionDefinition) {
|
||||||
append("fn ")
|
append("fn ")
|
||||||
visit(node.symbol)
|
visit(node.symbol)
|
||||||
append("(")
|
append("(")
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
rootProject.name = "pork"
|
rootProject.name = "pork"
|
||||||
|
|
||||||
|
includeBuild("buildext")
|
||||||
|
|
||||||
include(
|
include(
|
||||||
":common",
|
":common",
|
||||||
":ast",
|
":ast",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
plugins {
|
plugins {
|
||||||
application
|
application
|
||||||
pork_module
|
id("gay.pizza.pork.module")
|
||||||
id("com.github.johnrengelman.shadow") version "8.1.1"
|
id("com.github.johnrengelman.shadow") version "8.1.1"
|
||||||
id("org.graalvm.buildtools.native") version "0.9.25"
|
id("org.graalvm.buildtools.native") version "0.9.25"
|
||||||
}
|
}
|
||||||
@ -15,9 +15,19 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
application {
|
application {
|
||||||
|
applicationName = "pork"
|
||||||
mainClass.set("gay.pizza.pork.tool.MainKt")
|
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 {
|
graalvmNative {
|
||||||
binaries {
|
binaries {
|
||||||
named("main") {
|
named("main") {
|
||||||
|
Loading…
Reference in New Issue
Block a user