Multi-module arrangement and the start of AST generation.

This commit is contained in:
Alex Zenla 2023-09-03 23:15:21 -07:00
parent bf3967544a
commit d46ea1e307
Signed by: alex
GPG Key ID: C0780728420EBFE5
94 changed files with 377 additions and 138 deletions

View File

@ -23,5 +23,5 @@ fn main() {
## Usage
```
./gradlew -q run --args 'run examples/fib.pork'
./gradlew -q core:run --args 'run ../examples/fib.pork'
```

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

@ -1,7 +1,5 @@
package gay.pizza.pork.ast.nodes
package gay.pizza.pork.ast
import gay.pizza.pork.ast.NodeType
import gay.pizza.pork.ast.NodeVisitor
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@ -1,7 +1,5 @@
package gay.pizza.pork.ast.nodes
package gay.pizza.pork.ast
import gay.pizza.pork.ast.NodeType
import gay.pizza.pork.ast.NodeVisitor
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@ -1,6 +1,5 @@
package gay.pizza.pork.ast.nodes
package gay.pizza.pork.ast
import gay.pizza.pork.ast.NodeType
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@ -1,7 +1,5 @@
package gay.pizza.pork.ast.nodes
package gay.pizza.pork.ast
import gay.pizza.pork.ast.NodeType
import gay.pizza.pork.ast.NodeVisitor
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@ -1,4 +1,4 @@
package gay.pizza.pork.ast.nodes
package gay.pizza.pork.ast
import kotlinx.serialization.Serializable

View File

@ -1,4 +1,4 @@
package gay.pizza.pork.ast.nodes
package gay.pizza.pork.ast
import kotlinx.serialization.Serializable

View File

@ -1,4 +1,4 @@
package gay.pizza.pork.ast.nodes
package gay.pizza.pork.ast
import kotlinx.serialization.Serializable

View File

@ -1,4 +1,4 @@
package gay.pizza.pork.ast.nodes
package gay.pizza.pork.ast
import kotlinx.serialization.Serializable

View File

@ -1,7 +1,5 @@
package gay.pizza.pork.ast.nodes
package gay.pizza.pork.ast
import gay.pizza.pork.ast.NodeType
import gay.pizza.pork.ast.NodeVisitor
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@ -1,7 +1,5 @@
package gay.pizza.pork.ast.nodes
package gay.pizza.pork.ast
import gay.pizza.pork.ast.NodeType
import gay.pizza.pork.ast.NodeVisitor
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@ -1,7 +1,5 @@
package gay.pizza.pork.ast.nodes
package gay.pizza.pork.ast
import gay.pizza.pork.ast.NodeType
import gay.pizza.pork.ast.NodeVisitor
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@ -1,7 +1,5 @@
package gay.pizza.pork.ast.nodes
package gay.pizza.pork.ast
import gay.pizza.pork.ast.NodeType
import gay.pizza.pork.ast.NodeVisitor
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@ -1,7 +1,5 @@
package gay.pizza.pork.ast.nodes
package gay.pizza.pork.ast
import gay.pizza.pork.ast.NodeType
import gay.pizza.pork.ast.NodeVisitor
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@ -1,4 +1,4 @@
package gay.pizza.pork.ast.nodes
package gay.pizza.pork.ast
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@ -1,6 +1,5 @@
package gay.pizza.pork.ast.nodes
package gay.pizza.pork.ast
import gay.pizza.pork.ast.NodeType
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@ -1,7 +1,5 @@
package gay.pizza.pork.ast.nodes
package gay.pizza.pork.ast
import gay.pizza.pork.ast.NodeType
import gay.pizza.pork.ast.NodeVisitor
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@ -1,7 +1,5 @@
package gay.pizza.pork.ast.nodes
package gay.pizza.pork.ast
import gay.pizza.pork.ast.NodeType
import gay.pizza.pork.ast.NodeVisitor
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

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

@ -1,7 +1,5 @@
package gay.pizza.pork.ast
import gay.pizza.pork.ast.nodes.*
class NodeCoalescer(val handler: (Node) -> Unit) : NodeVisitor<Unit> {
override fun visitIntLiteral(node: IntLiteral): Unit = handle(node)
override fun visitStringLiteral(node: StringLiteral): Unit = handle(node)

View File

@ -1,7 +1,5 @@
package gay.pizza.pork.ast
import gay.pizza.pork.ast.nodes.*
interface NodeVisitor<T> {
fun visitIntLiteral(node: IntLiteral): T
fun visitStringLiteral(node: StringLiteral): T

View File

@ -1,7 +1,5 @@
package gay.pizza.pork.ast.nodes
package gay.pizza.pork.ast
import gay.pizza.pork.ast.NodeType
import gay.pizza.pork.ast.NodeVisitor
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@ -1,7 +1,5 @@
package gay.pizza.pork.ast.nodes
package gay.pizza.pork.ast
import gay.pizza.pork.ast.NodeType
import gay.pizza.pork.ast.NodeVisitor
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@ -1,4 +1,4 @@
package gay.pizza.pork.ast.nodes
package gay.pizza.pork.ast
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@ -1,6 +1,5 @@
package gay.pizza.pork.ast.nodes
package gay.pizza.pork.ast
import gay.pizza.pork.ast.NodeType
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@ -1,6 +1,5 @@
package gay.pizza.pork.ast.nodes
package gay.pizza.pork.ast
import gay.pizza.pork.ast.NodeType
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@ -1,7 +1,5 @@
package gay.pizza.pork.ast.nodes
package gay.pizza.pork.ast
import gay.pizza.pork.ast.NodeType
import gay.pizza.pork.ast.NodeVisitor
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@ -1,51 +1,3 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
application
kotlin("jvm") version "1.9.10"
kotlin("plugin.serialization") version "1.9.10"
id("com.github.johnrengelman.shadow") version "8.1.1"
id("org.graalvm.buildtools.native") version "0.9.25"
}
repositories {
mavenCentral()
}
java {
val javaVersion = JavaVersion.toVersion(17)
sourceCompatibility = javaVersion
targetCompatibility = javaVersion
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-bom")
implementation("com.github.ajalt.clikt:clikt:4.2.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
}
tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = "17"
}
tasks.withType<Wrapper> {
gradleVersion = "8.3"
}
application {
mainClass.set("gay.pizza.pork.cli.MainKt")
}
graalvmNative {
binaries {
named("main") {
imageName.set("pork")
mainClass.set("gay.pizza.pork.cli.MainKt")
sharedLibrary.set(false)
}
}
}
tasks.run.get().outputs.upToDateWhen { false }

29
buildSrc/build.gradle.kts Normal file
View File

@ -0,0 +1,29 @@
plugins {
`kotlin-dsl`
}
repositories {
mavenCentral()
gradlePluginPortal()
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.10")
implementation("org.jetbrains.kotlin:kotlin-serialization:1.9.10")
implementation("com.fasterxml.jackson.core:jackson-databind:2.15.2")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.15.2")
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.15.2")
}
gradlePlugin {
plugins {
create("pork_ast") {
id = "gay.pizza.pork.ast"
implementationClass = "gay.pizza.pork.gradle.PorkAstPlugin"
displayName = "Pork AST"
description = "AST generation code for pork"
}
}
}

View File

@ -0,0 +1,30 @@
package gay.pizza.pork.gradle
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.ast.AstDescription
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.TaskAction
import gay.pizza.pork.gradle.ast.AstWorld
import java.io.File
open class GenerateAstCode : DefaultTask() {
init {
outputs.upToDateWhen { false }
}
@get:InputFile
val astDescriptionFile: File = project.file("src/main/ast/pork.yml")
@TaskAction
fun generate() {
val astYamlText = astDescriptionFile.readText()
val mapper = ObjectMapper(YAMLFactory())
mapper.registerModules(KotlinModule.Builder().build())
val astDescription = mapper.readValue(astYamlText, AstDescription::class.java)
val world = AstWorld()
world.build(astDescription)
}
}

View File

@ -0,0 +1,10 @@
package gay.pizza.pork.gradle
import org.gradle.api.Plugin
import org.gradle.api.Project
class PorkAstPlugin : Plugin<Project> {
override fun apply(target: Project) {
target.tasks.create("generateAstCode", GenerateAstCode::class.java)
}
}

View File

@ -0,0 +1,6 @@
package gay.pizza.pork.gradle.ast
data class AstDescription(
val root: String,
val types: Map<String, AstTypeDescription>
)

View File

@ -0,0 +1,6 @@
package gay.pizza.pork.gradle.ast
enum class AstPrimitive(val id: kotlin.String) {
Boolean("Boolean"),
String("String")
}

View File

@ -0,0 +1,12 @@
package gay.pizza.pork.gradle.ast
class AstType(val name: String, var parent: AstType? = null) {
private val internalValues = mutableListOf<AstValue>()
val values: List<AstValue>
get() = internalValues
internal fun addValue(value: AstValue) {
internalValues.add(value)
}
}

View File

@ -0,0 +1,6 @@
package gay.pizza.pork.gradle.ast
data class AstTypeDescription(
val parent: String? = null,
val values: List<AstValueDescription> = emptyList()
)

View File

@ -0,0 +1,31 @@
package gay.pizza.pork.gradle.ast
class AstTypeRef(
val type: AstType? = null,
val primitive: AstPrimitive? = null,
val form: AstTypeRefForm
) {
companion object {
fun parse(input: String, registry: AstTypeRegistry): AstTypeRef {
if (input.startsWith("List<")) {
val underlyingType = input.substring(5, input.length - 1)
val underlyingRef = parse(underlyingType, registry)
return AstTypeRef(
type = underlyingRef.type,
primitive = underlyingRef.primitive,
form = AstTypeRefForm.List
)
}
val primitive = AstPrimitive.values().firstOrNull { it.name == input }
if (primitive != null) {
return AstTypeRef(
primitive = primitive,
form = AstTypeRefForm.Single
)
}
return AstTypeRef(type = registry.lookup(input), form = AstTypeRefForm.Single)
}
}
}

View File

@ -0,0 +1,6 @@
package gay.pizza.pork.gradle.ast
enum class AstTypeRefForm {
Single,
List
}

View File

@ -0,0 +1,32 @@
package gay.pizza.pork.gradle.ast
class AstTypeRegistry {
private val internalTypes = mutableSetOf<AstType>()
val types: Set<AstType>
get() = internalTypes
fun add(type: AstType): AstType {
internalTypes.add(type)
return type
}
fun lookupOrNull(name: String): AstType? =
internalTypes.singleOrNull { it.name == name }
fun lookup(name: String): AstType = lookupOrNull(name)
?: throw RuntimeException("Unknown AstType: $name")
fun roleOfType(type: AstType): AstTypeRole =
when {
type.parent == null && type.values.isNotEmpty() ->
AstTypeRole.RootNode
type.parent != null && type.values.all { it.abstract } ->
AstTypeRole.HierarchyNode
type.parent != null && type.values.none { it.abstract } ->
AstTypeRole.AstNode
type.parent == null && type.values.isNotEmpty() ->
AstTypeRole.ValueHolder
else -> throw RuntimeException("Unable to determine role of type ${type.name}")
}
}

View File

@ -0,0 +1,8 @@
package gay.pizza.pork.gradle.ast
enum class AstTypeRole {
RootNode,
HierarchyNode,
AstNode,
ValueHolder
}

View File

@ -0,0 +1,7 @@
package gay.pizza.pork.gradle.ast
class AstValue(
val name: String,
val typeRef: AstTypeRef,
val abstract: Boolean = false
)

View File

@ -0,0 +1,6 @@
package gay.pizza.pork.gradle.ast
data class AstValueDescription(
val name: String,
val type: String
)

View File

@ -0,0 +1,28 @@
package gay.pizza.pork.gradle.ast
class AstWorld {
val typeRegistry: AstTypeRegistry = AstTypeRegistry()
fun build(description: AstDescription) {
val rootType = typeRegistry.add(AstType(description.root))
for (typeName in description.types.keys) {
if (typeName == rootType.name) {
throw RuntimeException("Cannot have type with the same name as the root type.")
}
typeRegistry.add(AstType(typeName))
}
for ((typeName, typeDescription) in description.types) {
val type = typeRegistry.lookup(typeName)
if (typeDescription.parent != null) {
type.parent = typeRegistry.lookup(typeDescription.parent)
}
for (value in typeDescription.values) {
val typeRef = AstTypeRef.parse(value.type, typeRegistry)
val typeValue = AstValue(value.name, typeRef)
type.addValue(typeValue)
}
}
}
}

View File

@ -0,0 +1,25 @@
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")
}

27
core/build.gradle.kts Normal file
View File

@ -0,0 +1,27 @@
plugins {
application
pork_module
id("com.github.johnrengelman.shadow") version "8.1.1"
id("org.graalvm.buildtools.native") version "0.9.25"
}
dependencies {
api(project(":ast"))
implementation(libs.clikt)
}
application {
mainClass.set("gay.pizza.pork.cli.MainKt")
}
graalvmNative {
binaries {
named("main") {
imageName.set("pork")
mainClass.set("gay.pizza.pork.cli.MainKt")
sharedLibrary.set(false)
}
}
}
tasks.run.get().outputs.upToDateWhen { false }

View File

@ -3,7 +3,7 @@ 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.ast.Node
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json

View File

@ -1,8 +1,8 @@
package gay.pizza.pork.cli
import gay.pizza.pork.ast.NodeVisitor
import gay.pizza.pork.ast.Printer
import gay.pizza.pork.ast.nodes.CompilationUnit
import gay.pizza.pork.parse.Printer
import gay.pizza.pork.ast.CompilationUnit
import gay.pizza.pork.eval.*
import gay.pizza.pork.frontend.ContentSource
import gay.pizza.pork.frontend.World

View File

@ -1,7 +1,7 @@
package gay.pizza.pork.eval
import gay.pizza.pork.ast.nodes.CompilationUnit
import gay.pizza.pork.ast.nodes.ImportDeclaration
import gay.pizza.pork.ast.CompilationUnit
import gay.pizza.pork.ast.ImportDeclaration
class EvaluationContext(
val compilationUnit: CompilationUnit,

View File

@ -1,7 +1,6 @@
package gay.pizza.pork.eval
import gay.pizza.pork.ast.NodeVisitor
import gay.pizza.pork.ast.nodes.*
import gay.pizza.pork.ast.*
class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
private var currentScope: Scope = root
@ -65,7 +64,7 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
visit(node.thenExpression)
} else {
if (node.elseExpression != null) {
visit(node.elseExpression)
visit(node.elseExpression!!)
} else {
None
}

View File

@ -1,7 +1,7 @@
package gay.pizza.pork.frontend
import gay.pizza.pork.ast.nodes.CompilationUnit
import gay.pizza.pork.ast.nodes.ImportDeclaration
import gay.pizza.pork.ast.CompilationUnit
import gay.pizza.pork.ast.ImportDeclaration
import gay.pizza.pork.parse.DiscardNodeAttribution
import gay.pizza.pork.parse.Parser
import gay.pizza.pork.parse.TokenStreamSource

View File

@ -1,6 +1,6 @@
package gay.pizza.pork.parse
import gay.pizza.pork.ast.nodes.Node
import gay.pizza.pork.ast.Node
object DiscardNodeAttribution : NodeAttribution {
override fun enter() {}

View File

@ -1,6 +1,6 @@
package gay.pizza.pork.parse
import gay.pizza.pork.ast.nodes.Node
import gay.pizza.pork.ast.Node
interface NodeAttribution {
fun enter()

View File

@ -1,6 +1,6 @@
package gay.pizza.pork.parse
import gay.pizza.pork.ast.nodes.*
import gay.pizza.pork.ast.*
import gay.pizza.pork.util.StringEscape
class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {

View File

@ -1,6 +1,6 @@
package gay.pizza.pork.ast
package gay.pizza.pork.parse
import gay.pizza.pork.ast.nodes.*
import gay.pizza.pork.ast.*
import gay.pizza.pork.util.IndentPrinter
import gay.pizza.pork.util.StringEscape
@ -134,7 +134,7 @@ class Printer(buffer: StringBuilder) : NodeVisitor<Unit> {
append("else")
out.increaseIndent()
appendLine()
visit(node.elseExpression)
visit(node.elseExpression!!)
out.decreaseIndent()
}
}

View File

@ -1,7 +1,7 @@
package gay.pizza.pork.parse
import gay.pizza.pork.ast.NodeCoalescer
import gay.pizza.pork.ast.nodes.Node
import gay.pizza.pork.ast.Node
import java.util.IdentityHashMap
class TokenNodeAttribution : NodeAttribution {

View File

@ -1 +1,17 @@
rootProject.name = "pork"
include(
":ast",
":core"
)
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
version("clikt", "4.2.0")
library("clikt", "com.github.ajalt.clikt", "clikt")
.versionRef("clikt")
}
}
}

View File

@ -1,15 +0,0 @@
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
@Serializable
sealed class Node {
abstract val type: NodeType
open fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> = emptyList()
override fun toString(): String =
let { node -> buildString { Printer(this).visit(node) } }
}