Multi-module arrangement and the start of AST generation.

This commit is contained in:
2023-09-03 23:15:21 -07:00
parent bf3967544a
commit d46ea1e307
94 changed files with 377 additions and 138 deletions

5
ast/build.gradle.kts Normal file
View File

@ -0,0 +1,5 @@
plugins {
pork_module
id("gay.pizza.pork.ast")
}

38
ast/src/main/ast/pork.yml Normal file
View File

@ -0,0 +1,38 @@
root: Node
types:
Expression:
parent: Node
Symbol:
parent: Node
Declaration:
parent: Node
Definition:
parent: Node
values:
- name: symbol
type: Symbol
- name: modifiers
type: DefinitionModifiers
DefinitionModifiers:
values:
- name: export
type: Boolean
Block:
parent: Node
values:
- name: expressions
type: List<Expression>
CompilationUnit:
parent: Node
values:
- name: declarations
type: List<Declaration>
- name: definitions
type: List<Declaration>
Assignment:
parent: Expression
values:
- name: symbol
type: Symbol
- name: value
type: Expression

View File

@ -0,0 +1,25 @@
package gay.pizza.pork.ast
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("assignment")
class Assignment(val symbol: Symbol, val value: Expression) : Expression() {
override val type: NodeType = NodeType.Assignment
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
visitor.visitNodes(symbol, value)
override fun equals(other: Any?): Boolean {
if (other !is Assignment) return false
return other.symbol == symbol && other.value == value
}
override fun hashCode(): Int {
var result = symbol.hashCode()
result = 31 * result + value.hashCode()
result = 31 * result + type.hashCode()
return result
}
}

View File

@ -0,0 +1,24 @@
package gay.pizza.pork.ast
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("block")
class Block(val expressions: List<Expression>) : Node() {
override val type: NodeType = NodeType.Block
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
visitor.visitAll(expressions)
override fun equals(other: Any?): Boolean {
if (other !is Block) return false
return other.expressions == expressions
}
override fun hashCode(): Int {
var result = expressions.hashCode()
result = 31 * result + type.hashCode()
return result
}
}

View File

@ -0,0 +1,21 @@
package gay.pizza.pork.ast
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("booleanLiteral")
class BooleanLiteral(val value: Boolean) : Expression() {
override val type: NodeType = NodeType.BooleanLiteral
override fun equals(other: Any?): Boolean {
if (other !is BooleanLiteral) return false
return other.value == value
}
override fun hashCode(): Int {
var result = value.hashCode()
result = 31 * result + type.hashCode()
return result
}
}

View File

@ -0,0 +1,24 @@
package gay.pizza.pork.ast
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("compilationUnit")
class CompilationUnit(val declarations: List<Declaration>, val definitions: List<Definition>) : Node() {
override val type: NodeType = NodeType.CompilationUnit
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
visitor.visitAll(declarations)
override fun equals(other: Any?): Boolean {
if (other !is CompilationUnit) return false
return other.declarations == declarations
}
override fun hashCode(): Int {
var result = declarations.hashCode()
result = 31 * result + type.hashCode()
return result
}
}

View File

@ -0,0 +1,6 @@
package gay.pizza.pork.ast
import kotlinx.serialization.Serializable
@Serializable
sealed class Declaration : Node()

View File

@ -0,0 +1,9 @@
package gay.pizza.pork.ast
import kotlinx.serialization.Serializable
@Serializable
sealed class Definition : Node() {
abstract val symbol: Symbol
abstract val modifiers: DefinitionModifiers
}

View File

@ -0,0 +1,8 @@
package gay.pizza.pork.ast
import kotlinx.serialization.Serializable
@Serializable
class DefinitionModifiers(
var export: Boolean
)

View File

@ -0,0 +1,6 @@
package gay.pizza.pork.ast
import kotlinx.serialization.Serializable
@Serializable
sealed class Expression : Node()

View File

@ -0,0 +1,25 @@
package gay.pizza.pork.ast
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("functionCall")
class FunctionCall(val symbol: Symbol, val arguments: List<Expression>) : Expression() {
override val type: NodeType = NodeType.FunctionCall
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
visitor.visitAll(listOf(symbol), arguments)
override fun equals(other: Any?): Boolean {
if (other !is FunctionCall) return false
return other.symbol == symbol && other.arguments == arguments
}
override fun hashCode(): Int {
var result = symbol.hashCode()
result = 31 * result + arguments.hashCode()
result = 31 * result + type.hashCode()
return result
}
}

View File

@ -0,0 +1,31 @@
package gay.pizza.pork.ast
import kotlinx.serialization.SerialName
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() {
override val type: NodeType = NodeType.FunctionDeclaration
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
visitor.visitNodes(symbol)
override fun equals(other: Any?): Boolean {
if (other !is FunctionDefinition) return false
return other.symbol == symbol && other.arguments == arguments && other.block == block
}
override fun hashCode(): Int {
var result = symbol.hashCode()
result = 31 * result + arguments.hashCode()
result = 31 * result + block.hashCode()
result = 31 * result + type.hashCode()
return result
}
}

View File

@ -0,0 +1,32 @@
package gay.pizza.pork.ast
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("if")
class If(
val condition: Expression,
val thenExpression: Expression,
val elseExpression: Expression? = null
) : Expression() {
override val type: NodeType = NodeType.If
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
visitor.visitNodes(condition, thenExpression, elseExpression)
override fun equals(other: Any?): Boolean {
if (other !is If) return false
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 + type.hashCode()
return result
}
}

View File

@ -0,0 +1,24 @@
package gay.pizza.pork.ast
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("importDeclaration")
class ImportDeclaration(val path: StringLiteral) : Declaration() {
override val type: NodeType = NodeType.ImportDeclaration
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
visitor.visitNodes(path)
override fun equals(other: Any?): Boolean {
if (other !is ImportDeclaration) return false
return other.path == path
}
override fun hashCode(): Int {
var result = path.hashCode()
result = 31 * result + type.hashCode()
return result
}
}

View File

@ -0,0 +1,32 @@
package gay.pizza.pork.ast
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("infixOperation")
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> =
visitor.visitNodes(left, right)
override fun equals(other: Any?): Boolean {
if (other !is InfixOperation) return false
return other.op == op &&
other.left == left &&
other.right == right
}
override fun hashCode(): Int {
var result = left.hashCode()
result = 31 * result + op.hashCode()
result = 31 * result + right.hashCode()
result = 31 * result + type.hashCode()
return result
}
}

View File

@ -0,0 +1,15 @@
package gay.pizza.pork.ast
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("infixOperator")
enum class InfixOperator(val token: String) {
Plus("+"),
Minus("-"),
Multiply("*"),
Divide("/"),
Equals("=="),
NotEquals("!=")
}

View File

@ -0,0 +1,21 @@
package gay.pizza.pork.ast
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("intLiteral")
class IntLiteral(val value: Int) : Expression() {
override val type: NodeType = NodeType.IntLiteral
override fun equals(other: Any?): Boolean {
if (other !is IntLiteral) return false
return other.value == value
}
override fun hashCode(): Int {
var result = value
result = 31 * result + type.hashCode()
return result
}
}

View File

@ -0,0 +1,25 @@
package gay.pizza.pork.ast
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("lambda")
class Lambda(val arguments: List<Symbol>, val expressions: List<Expression>) : Expression() {
override val type: NodeType = NodeType.Lambda
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
visitor.visitAll(arguments, expressions)
override fun equals(other: Any?): Boolean {
if (other !is Lambda) return false
return other.arguments == arguments && other.expressions == expressions
}
override fun hashCode(): Int {
var result = arguments.hashCode()
result = 31 * result + expressions.hashCode()
result = 31 * result + type.hashCode()
return result
}
}

View File

@ -0,0 +1,24 @@
package gay.pizza.pork.ast
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("listLiteral")
class ListLiteral(val items: List<Expression>) : Expression() {
override val type: NodeType = NodeType.ListLiteral
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
visitor.visitAll(items)
override fun equals(other: Any?): Boolean {
if (other !is ListLiteral) return false
return other.items == items
}
override fun hashCode(): Int {
var result = items.hashCode()
result = 31 * result + type.hashCode()
return result
}
}

View File

@ -0,0 +1,9 @@
package gay.pizza.pork.ast
import kotlinx.serialization.Serializable
@Serializable
sealed class Node {
abstract val type: NodeType
open fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> = emptyList()
}

View File

@ -0,0 +1,27 @@
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 visitDefine(node: Assignment): 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)
private fun handle(node: Node) {
handler(node)
node.visitChildren(this)
}
}

View File

@ -0,0 +1,42 @@
package gay.pizza.pork.ast
enum class NodeType(val parent: NodeType? = null) {
Node,
Symbol(Node),
Expression(Node),
Declaration(Node),
Block(Node),
CompilationUnit(Node),
IntLiteral(Expression),
BooleanLiteral(Expression),
ListLiteral(Expression),
StringLiteral(Expression),
Parentheses(Expression),
Assignment(Expression),
Lambda(Expression),
PrefixOperation(Expression),
InfixOperation(Expression),
SymbolReference(Expression),
FunctionCall(Expression),
If(Expression),
ImportDeclaration(Declaration),
FunctionDeclaration(Declaration);
val parents: Set<NodeType>
init {
val calculatedParents = mutableListOf<NodeType>()
var self = this
while (true) {
calculatedParents.add(self)
if (self.parent != null) {
self = self.parent!!
} else {
break
}
}
parents = calculatedParents.toSet()
}
fun isa(type: NodeType): Boolean = this == type || parents.contains(type)
}

View File

@ -0,0 +1,60 @@
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 visitDefine(node: Assignment): 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 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 Assignment -> visitDefine(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) {
is ImportDeclaration -> visitImportDeclaration(node)
}
fun visitDefinition(node: Definition): T = when (node) {
is FunctionDefinition -> visitFunctionDeclaration(node)
}
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 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()
}

View File

@ -0,0 +1,24 @@
package gay.pizza.pork.ast
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("parentheses")
class Parentheses(val expression: Expression) : Expression() {
override val type: NodeType = NodeType.Parentheses
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
visitor.visitNodes(expression)
override fun equals(other: Any?): Boolean {
if (other !is Parentheses) return false
return other.expression == expression
}
override fun hashCode(): Int {
var result = expression.hashCode()
result = 31 * result + type.hashCode()
return result
}
}

View File

@ -0,0 +1,25 @@
package gay.pizza.pork.ast
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("prefixOperation")
class PrefixOperation(val op: PrefixOperator, val expression: Expression) : Expression() {
override val type: NodeType = NodeType.PrefixOperation
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
visitor.visitNodes(expression)
override fun equals(other: Any?): Boolean {
if (other !is PrefixOperation) return false
return other.op == op && other.expression == expression
}
override fun hashCode(): Int {
var result = op.hashCode()
result = 31 * result + expression.hashCode()
result = 31 * result + type.hashCode()
return result
}
}

View File

@ -0,0 +1,10 @@
package gay.pizza.pork.ast
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("prefixOperator")
enum class PrefixOperator(val token: String) {
Negate("!")
}

View File

@ -0,0 +1,21 @@
package gay.pizza.pork.ast
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("stringLiteral")
class StringLiteral(val text: String) : Expression() {
override val type: NodeType = NodeType.StringLiteral
override fun equals(other: Any?): Boolean {
if (other !is StringLiteral) return false
return other.text == text
}
override fun hashCode(): Int {
var result = text.hashCode()
result = 31 * result + type.hashCode()
return result
}
}

View File

@ -0,0 +1,21 @@
package gay.pizza.pork.ast
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("symbol")
class Symbol(val id: String) : Node() {
override val type: NodeType = NodeType.Symbol
override fun equals(other: Any?): Boolean {
if (other !is Symbol) return false
return other.id == id
}
override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + type.hashCode()
return result
}
}

View File

@ -0,0 +1,24 @@
package gay.pizza.pork.ast
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("symbolReference")
class SymbolReference(val symbol: Symbol) : Expression() {
override val type: NodeType = NodeType.SymbolReference
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
visitor.visitNodes(symbol)
override fun equals(other: Any?): Boolean {
if (other !is SymbolReference) return false
return other.symbol == symbol
}
override fun hashCode(): Int {
var result = symbol.hashCode()
result = 31 * result + type.hashCode()
return result
}
}