diff --git a/build.gradle.kts b/build.gradle.kts index a711d5b..60f2fb9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -24,6 +24,7 @@ dependencies { implementation("org.jetbrains.kotlin:kotlin-bom") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") implementation("com.github.ajalt.clikt:clikt:4.2.0") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0-RC") } tasks.withType { diff --git a/src/main/kotlin/gay/pizza/pork/ast/nodes/BooleanLiteral.kt b/src/main/kotlin/gay/pizza/pork/ast/nodes/BooleanLiteral.kt index f2368d8..86ce0a7 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/nodes/BooleanLiteral.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/nodes/BooleanLiteral.kt @@ -1,7 +1,11 @@ package gay.pizza.pork.ast.nodes import gay.pizza.pork.ast.NodeType +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable +@SerialName("booleanLiteral") class BooleanLiteral(val value: Boolean) : Expression() { override val type: NodeType = NodeType.BooleanLiteral diff --git a/src/main/kotlin/gay/pizza/pork/ast/nodes/Define.kt b/src/main/kotlin/gay/pizza/pork/ast/nodes/Define.kt index adc8d2d..334af6b 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/nodes/Define.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/nodes/Define.kt @@ -2,7 +2,11 @@ package gay.pizza.pork.ast.nodes import gay.pizza.pork.ast.NodeType 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() { override val type: NodeType = NodeType.Define diff --git a/src/main/kotlin/gay/pizza/pork/ast/nodes/Expression.kt b/src/main/kotlin/gay/pizza/pork/ast/nodes/Expression.kt index a32dfbc..9e15fd5 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/nodes/Expression.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/nodes/Expression.kt @@ -1,3 +1,6 @@ package gay.pizza.pork.ast.nodes -abstract class Expression : Node() +import kotlinx.serialization.Serializable + +@Serializable +sealed class Expression : Node() diff --git a/src/main/kotlin/gay/pizza/pork/ast/nodes/FunctionCall.kt b/src/main/kotlin/gay/pizza/pork/ast/nodes/FunctionCall.kt index 12b23b3..e0ba161 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/nodes/FunctionCall.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/nodes/FunctionCall.kt @@ -2,7 +2,11 @@ package gay.pizza.pork.ast.nodes import gay.pizza.pork.ast.NodeType 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() { override val type: NodeType = NodeType.FunctionCall @@ -13,4 +17,11 @@ class FunctionCall(val symbol: Symbol, val arguments: List) : Expres 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 + } } diff --git a/src/main/kotlin/gay/pizza/pork/ast/nodes/If.kt b/src/main/kotlin/gay/pizza/pork/ast/nodes/If.kt index aed20ee..6cb010e 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/nodes/If.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/nodes/If.kt @@ -2,7 +2,11 @@ package gay.pizza.pork.ast.nodes import gay.pizza.pork.ast.NodeType import gay.pizza.pork.ast.NodeVisitor +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable +@SerialName("if") class If( val condition: Expression, val thenExpression: Expression, diff --git a/src/main/kotlin/gay/pizza/pork/ast/nodes/InfixOperation.kt b/src/main/kotlin/gay/pizza/pork/ast/nodes/InfixOperation.kt index 043d3bb..9fb9d47 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/nodes/InfixOperation.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/nodes/InfixOperation.kt @@ -2,7 +2,11 @@ package gay.pizza.pork.ast.nodes import gay.pizza.pork.ast.NodeType import gay.pizza.pork.ast.NodeVisitor +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable +@SerialName("infixOperation") class InfixOperation( val left: Expression, val op: InfixOperator, diff --git a/src/main/kotlin/gay/pizza/pork/ast/nodes/InfixOperator.kt b/src/main/kotlin/gay/pizza/pork/ast/nodes/InfixOperator.kt index d311697..e5b971e 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/nodes/InfixOperator.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/nodes/InfixOperator.kt @@ -1,5 +1,10 @@ package gay.pizza.pork.ast.nodes +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +@SerialName("infixOperator") enum class InfixOperator(val token: String) { Plus("+"), Minus("-"), diff --git a/src/main/kotlin/gay/pizza/pork/ast/nodes/IntLiteral.kt b/src/main/kotlin/gay/pizza/pork/ast/nodes/IntLiteral.kt index 4966b39..541a214 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/nodes/IntLiteral.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/nodes/IntLiteral.kt @@ -1,7 +1,11 @@ package gay.pizza.pork.ast.nodes import gay.pizza.pork.ast.NodeType +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable +@SerialName("intLiteral") class IntLiteral(val value: Int) : Expression() { override val type: NodeType = NodeType.IntLiteral diff --git a/src/main/kotlin/gay/pizza/pork/ast/nodes/Lambda.kt b/src/main/kotlin/gay/pizza/pork/ast/nodes/Lambda.kt index 156de36..d23254b 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/nodes/Lambda.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/nodes/Lambda.kt @@ -2,7 +2,11 @@ package gay.pizza.pork.ast.nodes import gay.pizza.pork.ast.NodeType import gay.pizza.pork.ast.NodeVisitor +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable +@SerialName("lambda") class Lambda(val arguments: List, val expressions: List) : Expression() { override val type: NodeType = NodeType.Lambda diff --git a/src/main/kotlin/gay/pizza/pork/ast/nodes/ListLiteral.kt b/src/main/kotlin/gay/pizza/pork/ast/nodes/ListLiteral.kt index 64a2b1a..ae848f1 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/nodes/ListLiteral.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/nodes/ListLiteral.kt @@ -2,7 +2,11 @@ package gay.pizza.pork.ast.nodes import gay.pizza.pork.ast.NodeType import gay.pizza.pork.ast.NodeVisitor +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable +@SerialName("listLiteral") class ListLiteral(val items: List) : Expression() { override val type: NodeType = NodeType.ListLiteral diff --git a/src/main/kotlin/gay/pizza/pork/ast/nodes/Node.kt b/src/main/kotlin/gay/pizza/pork/ast/nodes/Node.kt index 1cfbb72..a7d2f6f 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/nodes/Node.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/nodes/Node.kt @@ -3,8 +3,10 @@ package gay.pizza.pork.ast.nodes import gay.pizza.pork.ast.NodeType import gay.pizza.pork.ast.Printer import gay.pizza.pork.ast.NodeVisitor +import kotlinx.serialization.Serializable -abstract class Node { +@Serializable +sealed class Node { abstract val type: NodeType open fun visitChildren(visitor: NodeVisitor): List = emptyList() diff --git a/src/main/kotlin/gay/pizza/pork/ast/nodes/Parentheses.kt b/src/main/kotlin/gay/pizza/pork/ast/nodes/Parentheses.kt index b7be5fc..7e1f9b4 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/nodes/Parentheses.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/nodes/Parentheses.kt @@ -2,7 +2,11 @@ package gay.pizza.pork.ast.nodes import gay.pizza.pork.ast.NodeType import gay.pizza.pork.ast.NodeVisitor +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable +@SerialName("parentheses") class Parentheses(val expression: Expression) : Expression() { override val type: NodeType = NodeType.Parentheses diff --git a/src/main/kotlin/gay/pizza/pork/ast/nodes/PrefixOperation.kt b/src/main/kotlin/gay/pizza/pork/ast/nodes/PrefixOperation.kt index dd00638..e025225 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/nodes/PrefixOperation.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/nodes/PrefixOperation.kt @@ -2,7 +2,11 @@ package gay.pizza.pork.ast.nodes import gay.pizza.pork.ast.NodeType 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() { override val type: NodeType = NodeType.PrefixOperation diff --git a/src/main/kotlin/gay/pizza/pork/ast/nodes/PrefixOperator.kt b/src/main/kotlin/gay/pizza/pork/ast/nodes/PrefixOperator.kt index 7bd9d75..051b7cf 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/nodes/PrefixOperator.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/nodes/PrefixOperator.kt @@ -1,5 +1,10 @@ package gay.pizza.pork.ast.nodes +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +@SerialName("prefixOperator") enum class PrefixOperator(val token: String) { Negate("!") } diff --git a/src/main/kotlin/gay/pizza/pork/ast/nodes/Program.kt b/src/main/kotlin/gay/pizza/pork/ast/nodes/Program.kt index 586b922..a664124 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/nodes/Program.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/nodes/Program.kt @@ -2,7 +2,11 @@ package gay.pizza.pork.ast.nodes import gay.pizza.pork.ast.NodeType import gay.pizza.pork.ast.NodeVisitor +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable +@SerialName("program") class Program(val expressions: List) : Node() { override val type: NodeType = NodeType.Program diff --git a/src/main/kotlin/gay/pizza/pork/ast/nodes/Symbol.kt b/src/main/kotlin/gay/pizza/pork/ast/nodes/Symbol.kt index 6efaf42..5d81b1f 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/nodes/Symbol.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/nodes/Symbol.kt @@ -1,7 +1,11 @@ package gay.pizza.pork.ast.nodes import gay.pizza.pork.ast.NodeType +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable +@SerialName("symbol") class Symbol(val id: String) : Node() { override val type: NodeType = NodeType.Symbol diff --git a/src/main/kotlin/gay/pizza/pork/ast/nodes/SymbolReference.kt b/src/main/kotlin/gay/pizza/pork/ast/nodes/SymbolReference.kt index 3156376..ab9deb7 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/nodes/SymbolReference.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/nodes/SymbolReference.kt @@ -2,7 +2,11 @@ package gay.pizza.pork.ast.nodes import gay.pizza.pork.ast.NodeType import gay.pizza.pork.ast.NodeVisitor +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable +@SerialName("symbolReference") class SymbolReference(val symbol: Symbol) : Expression() { override val type: NodeType = NodeType.SymbolReference diff --git a/src/main/kotlin/gay/pizza/pork/cli/AstCommand.kt b/src/main/kotlin/gay/pizza/pork/cli/AstCommand.kt new file mode 100644 index 0000000..fbd87c0 --- /dev/null +++ b/src/main/kotlin/gay/pizza/pork/cli/AstCommand.kt @@ -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)) + } +} diff --git a/src/main/kotlin/gay/pizza/pork/cli/RootCommand.kt b/src/main/kotlin/gay/pizza/pork/cli/RootCommand.kt index 69f82cb..fae9406 100644 --- a/src/main/kotlin/gay/pizza/pork/cli/RootCommand.kt +++ b/src/main/kotlin/gay/pizza/pork/cli/RootCommand.kt @@ -13,6 +13,7 @@ class RootCommand : CliktCommand( HighlightCommand(), TokenizeCommand(), ReprintCommand(), + AstCommand(), GenerateKotlinCommand() ) }