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

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")
}