mirror of
https://github.com/GayPizzaSpecifications/pork.git
synced 2025-08-03 13:11:32 +00:00
Multi-module arrangement and the start of AST generation.
This commit is contained in:
29
buildSrc/build.gradle.kts
Normal file
29
buildSrc/build.gradle.kts
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package gay.pizza.pork.gradle.ast
|
||||
|
||||
data class AstDescription(
|
||||
val root: String,
|
||||
val types: Map<String, AstTypeDescription>
|
||||
)
|
@ -0,0 +1,6 @@
|
||||
package gay.pizza.pork.gradle.ast
|
||||
|
||||
enum class AstPrimitive(val id: kotlin.String) {
|
||||
Boolean("Boolean"),
|
||||
String("String")
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package gay.pizza.pork.gradle.ast
|
||||
|
||||
data class AstTypeDescription(
|
||||
val parent: String? = null,
|
||||
val values: List<AstValueDescription> = emptyList()
|
||||
)
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package gay.pizza.pork.gradle.ast
|
||||
|
||||
enum class AstTypeRefForm {
|
||||
Single,
|
||||
List
|
||||
}
|
@ -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}")
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package gay.pizza.pork.gradle.ast
|
||||
|
||||
enum class AstTypeRole {
|
||||
RootNode,
|
||||
HierarchyNode,
|
||||
AstNode,
|
||||
ValueHolder
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package gay.pizza.pork.gradle.ast
|
||||
|
||||
class AstValue(
|
||||
val name: String,
|
||||
val typeRef: AstTypeRef,
|
||||
val abstract: Boolean = false
|
||||
)
|
@ -0,0 +1,6 @@
|
||||
package gay.pizza.pork.gradle.ast
|
||||
|
||||
data class AstValueDescription(
|
||||
val name: String,
|
||||
val type: String
|
||||
)
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
25
buildSrc/src/main/kotlin/pork_module.gradle.kts
Normal file
25
buildSrc/src/main/kotlin/pork_module.gradle.kts
Normal 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")
|
||||
}
|
Reference in New Issue
Block a user