Implement AST serialization.

This commit is contained in:
2023-08-21 23:31:40 -07:00
parent 05cb52bc32
commit 4ec53e1209
20 changed files with 109 additions and 2 deletions

View File

@ -24,6 +24,7 @@ dependencies {
implementation("org.jetbrains.kotlin:kotlin-bom") implementation("org.jetbrains.kotlin:kotlin-bom")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("com.github.ajalt.clikt:clikt:4.2.0") implementation("com.github.ajalt.clikt:clikt:4.2.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0-RC")
} }
tasks.withType<KotlinCompile> { tasks.withType<KotlinCompile> {

View File

@ -1,7 +1,11 @@
package gay.pizza.pork.ast.nodes package gay.pizza.pork.ast.nodes
import gay.pizza.pork.ast.NodeType import gay.pizza.pork.ast.NodeType
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("booleanLiteral")
class BooleanLiteral(val value: Boolean) : Expression() { class BooleanLiteral(val value: Boolean) : Expression() {
override val type: NodeType = NodeType.BooleanLiteral override val type: NodeType = NodeType.BooleanLiteral

View File

@ -2,7 +2,11 @@ package gay.pizza.pork.ast.nodes
import gay.pizza.pork.ast.NodeType import gay.pizza.pork.ast.NodeType
import gay.pizza.pork.ast.NodeVisitor import gay.pizza.pork.ast.NodeVisitor
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("define")
class Define(val symbol: Symbol, val value: Expression) : Expression() { class Define(val symbol: Symbol, val value: Expression) : Expression() {
override val type: NodeType = NodeType.Define override val type: NodeType = NodeType.Define

View File

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

View File

@ -2,7 +2,11 @@ package gay.pizza.pork.ast.nodes
import gay.pizza.pork.ast.NodeType import gay.pizza.pork.ast.NodeType
import gay.pizza.pork.ast.NodeVisitor import gay.pizza.pork.ast.NodeVisitor
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("functionCall")
class FunctionCall(val symbol: Symbol, val arguments: List<Expression>) : Expression() { class FunctionCall(val symbol: Symbol, val arguments: List<Expression>) : Expression() {
override val type: NodeType = NodeType.FunctionCall override val type: NodeType = NodeType.FunctionCall
@ -13,4 +17,11 @@ class FunctionCall(val symbol: Symbol, val arguments: List<Expression>) : Expres
if (other !is FunctionCall) return false if (other !is FunctionCall) return false
return other.symbol == symbol && other.arguments == arguments 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

@ -2,7 +2,11 @@ package gay.pizza.pork.ast.nodes
import gay.pizza.pork.ast.NodeType import gay.pizza.pork.ast.NodeType
import gay.pizza.pork.ast.NodeVisitor import gay.pizza.pork.ast.NodeVisitor
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("if")
class If( class If(
val condition: Expression, val condition: Expression,
val thenExpression: Expression, val thenExpression: Expression,

View File

@ -2,7 +2,11 @@ package gay.pizza.pork.ast.nodes
import gay.pizza.pork.ast.NodeType import gay.pizza.pork.ast.NodeType
import gay.pizza.pork.ast.NodeVisitor import gay.pizza.pork.ast.NodeVisitor
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("infixOperation")
class InfixOperation( class InfixOperation(
val left: Expression, val left: Expression,
val op: InfixOperator, val op: InfixOperator,

View File

@ -1,5 +1,10 @@
package gay.pizza.pork.ast.nodes package gay.pizza.pork.ast.nodes
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("infixOperator")
enum class InfixOperator(val token: String) { enum class InfixOperator(val token: String) {
Plus("+"), Plus("+"),
Minus("-"), Minus("-"),

View File

@ -1,7 +1,11 @@
package gay.pizza.pork.ast.nodes package gay.pizza.pork.ast.nodes
import gay.pizza.pork.ast.NodeType import gay.pizza.pork.ast.NodeType
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("intLiteral")
class IntLiteral(val value: Int) : Expression() { class IntLiteral(val value: Int) : Expression() {
override val type: NodeType = NodeType.IntLiteral override val type: NodeType = NodeType.IntLiteral

View File

@ -2,7 +2,11 @@ package gay.pizza.pork.ast.nodes
import gay.pizza.pork.ast.NodeType import gay.pizza.pork.ast.NodeType
import gay.pizza.pork.ast.NodeVisitor import gay.pizza.pork.ast.NodeVisitor
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("lambda")
class Lambda(val arguments: List<Symbol>, val expressions: List<Expression>) : Expression() { class Lambda(val arguments: List<Symbol>, val expressions: List<Expression>) : Expression() {
override val type: NodeType = NodeType.Lambda override val type: NodeType = NodeType.Lambda

View File

@ -2,7 +2,11 @@ package gay.pizza.pork.ast.nodes
import gay.pizza.pork.ast.NodeType import gay.pizza.pork.ast.NodeType
import gay.pizza.pork.ast.NodeVisitor import gay.pizza.pork.ast.NodeVisitor
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("listLiteral")
class ListLiteral(val items: List<Expression>) : Expression() { class ListLiteral(val items: List<Expression>) : Expression() {
override val type: NodeType = NodeType.ListLiteral override val type: NodeType = NodeType.ListLiteral

View File

@ -3,8 +3,10 @@ package gay.pizza.pork.ast.nodes
import gay.pizza.pork.ast.NodeType import gay.pizza.pork.ast.NodeType
import gay.pizza.pork.ast.Printer import gay.pizza.pork.ast.Printer
import gay.pizza.pork.ast.NodeVisitor import gay.pizza.pork.ast.NodeVisitor
import kotlinx.serialization.Serializable
abstract class Node { @Serializable
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()

View File

@ -2,7 +2,11 @@ package gay.pizza.pork.ast.nodes
import gay.pizza.pork.ast.NodeType import gay.pizza.pork.ast.NodeType
import gay.pizza.pork.ast.NodeVisitor import gay.pizza.pork.ast.NodeVisitor
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("parentheses")
class Parentheses(val expression: Expression) : Expression() { class Parentheses(val expression: Expression) : Expression() {
override val type: NodeType = NodeType.Parentheses override val type: NodeType = NodeType.Parentheses

View File

@ -2,7 +2,11 @@ package gay.pizza.pork.ast.nodes
import gay.pizza.pork.ast.NodeType import gay.pizza.pork.ast.NodeType
import gay.pizza.pork.ast.NodeVisitor import gay.pizza.pork.ast.NodeVisitor
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("prefixOperation")
class PrefixOperation(val op: PrefixOperator, val expression: Expression) : Expression() { class PrefixOperation(val op: PrefixOperator, val expression: Expression) : Expression() {
override val type: NodeType = NodeType.PrefixOperation override val type: NodeType = NodeType.PrefixOperation

View File

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

View File

@ -2,7 +2,11 @@ package gay.pizza.pork.ast.nodes
import gay.pizza.pork.ast.NodeType import gay.pizza.pork.ast.NodeType
import gay.pizza.pork.ast.NodeVisitor import gay.pizza.pork.ast.NodeVisitor
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("program")
class Program(val expressions: List<Expression>) : Node() { class Program(val expressions: List<Expression>) : Node() {
override val type: NodeType = NodeType.Program override val type: NodeType = NodeType.Program

View File

@ -1,7 +1,11 @@
package gay.pizza.pork.ast.nodes package gay.pizza.pork.ast.nodes
import gay.pizza.pork.ast.NodeType import gay.pizza.pork.ast.NodeType
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("symbol")
class Symbol(val id: String) : Node() { class Symbol(val id: String) : Node() {
override val type: NodeType = NodeType.Symbol override val type: NodeType = NodeType.Symbol

View File

@ -2,7 +2,11 @@ package gay.pizza.pork.ast.nodes
import gay.pizza.pork.ast.NodeType import gay.pizza.pork.ast.NodeType
import gay.pizza.pork.ast.NodeVisitor import gay.pizza.pork.ast.NodeVisitor
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
@SerialName("symbolReference")
class SymbolReference(val symbol: Symbol) : Expression() { class SymbolReference(val symbol: Symbol) : Expression() {
override val type: NodeType = NodeType.SymbolReference override val type: NodeType = NodeType.SymbolReference

View File

@ -0,0 +1,31 @@
package gay.pizza.pork.cli
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.types.path
import gay.pizza.pork.ast.nodes.Node
import gay.pizza.pork.parse.PorkParser
import gay.pizza.pork.parse.PorkTokenizer
import gay.pizza.pork.parse.StringCharSource
import gay.pizza.pork.parse.TokenStreamSource
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import kotlin.io.path.readText
@OptIn(ExperimentalSerializationApi::class)
class AstCommand : CliktCommand(help = "Print AST", name = "ast") {
val path by argument("file").path(mustExist = true, canBeDir = false)
private val json = Json {
prettyPrint = true
prettyPrintIndent = " "
classDiscriminator = "\$"
}
override fun run() {
val content = path.readText()
val tokenStream = PorkTokenizer(StringCharSource(content)).tokenize()
val program = PorkParser(TokenStreamSource(tokenStream)).readProgram()
println(json.encodeToString(Node.serializer(), program))
}
}

View File

@ -13,6 +13,7 @@ class RootCommand : CliktCommand(
HighlightCommand(), HighlightCommand(),
TokenizeCommand(), TokenizeCommand(),
ReprintCommand(), ReprintCommand(),
AstCommand(),
GenerateKotlinCommand() GenerateKotlinCommand()
) )
} }