mirror of
https://github.com/GayPizzaSpecifications/pork.git
synced 2025-08-03 13:11:32 +00:00
ast: generate graph
This commit is contained in:
@ -1,12 +1,18 @@
|
||||
package gay.pizza.pork.buildext
|
||||
|
||||
import gay.pizza.pork.buildext.ast.AstCodegen
|
||||
import gay.pizza.pork.buildext.ast.AstGraph
|
||||
import gay.pizza.pork.buildext.ast.AstWorld
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import java.io.File
|
||||
import kotlin.io.path.createDirectories
|
||||
import kotlin.io.path.deleteIfExists
|
||||
import kotlin.io.path.writeText
|
||||
|
||||
open class GenerateAstCode : DefaultTask() {
|
||||
init {
|
||||
@ -22,8 +28,18 @@ open class GenerateAstCode : DefaultTask() {
|
||||
@get:OutputDirectory
|
||||
var outputDirectory: File = project.file("src/main/kotlin/gay/pizza/pork/ast")
|
||||
|
||||
@get:OutputFile
|
||||
var typeGraphFile: File = project.file("src/main/graph/types.dot")
|
||||
|
||||
@TaskAction
|
||||
fun generate() {
|
||||
AstCodegen.run(codePackage, astDescriptionFile.toPath(), outputDirectory.toPath())
|
||||
val world = AstWorld.read(astDescriptionFile.toPath())
|
||||
AstCodegen.run(codePackage, world, outputDirectory.toPath())
|
||||
|
||||
val typeGraphPath = typeGraphFile.toPath()
|
||||
typeGraphPath.deleteIfExists()
|
||||
typeGraphPath.parent.createDirectories()
|
||||
val graph = AstGraph.from(world)
|
||||
typeGraphPath.writeText(graph.renderDotGraph())
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,6 @@
|
||||
@file:Suppress("ConstPropertyName")
|
||||
package gay.pizza.pork.buildext.ast
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
|
||||
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
||||
import gay.pizza.pork.buildext.codegen.*
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.nio.file.Path
|
||||
@ -407,15 +404,10 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
||||
companion object {
|
||||
private const val enableVisitAnyInline = false
|
||||
|
||||
fun run(pkg: String, astDescriptionFile: Path, outputDirectory: Path) {
|
||||
fun run(pkg: String, world: AstWorld, outputDirectory: Path) {
|
||||
if (!outputDirectory.exists()) {
|
||||
outputDirectory.createDirectories()
|
||||
}
|
||||
val astYamlText = astDescriptionFile.readText()
|
||||
val mapper = ObjectMapper(YAMLFactory())
|
||||
mapper.registerModules(KotlinModule.Builder().build())
|
||||
val astDescription = mapper.readValue(astYamlText, AstDescription::class.java)
|
||||
val world = AstWorld.build(astDescription)
|
||||
val codegen = AstCodegen(pkg, outputDirectory, world)
|
||||
codegen.generate()
|
||||
}
|
||||
|
@ -0,0 +1,58 @@
|
||||
package gay.pizza.pork.buildext.ast
|
||||
|
||||
class AstGraph {
|
||||
private val nodes = mutableSetOf<AstType>()
|
||||
private val children = mutableMapOf<AstType, MutableSet<AstType>>()
|
||||
private val valueReferences = mutableMapOf<AstType, MutableSet<AstType>>()
|
||||
|
||||
fun add(type: AstType) {
|
||||
nodes.add(type)
|
||||
if (type.parent != null) {
|
||||
children.getOrPut(type.parent!!) {
|
||||
mutableSetOf()
|
||||
}.add(type)
|
||||
add(type.parent!!)
|
||||
}
|
||||
|
||||
if (type.values != null) {
|
||||
for (value in type.values!!) {
|
||||
if (value.typeRef.type != null) {
|
||||
valueReferences.getOrPut(type) {
|
||||
mutableSetOf()
|
||||
}.add(value.typeRef.type)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun renderDotGraph(): String = buildString {
|
||||
appendLine("digraph A {")
|
||||
for (node in nodes) {
|
||||
appendLine(" type_${node.name} [shape=box,label=\"${node.name}\"]")
|
||||
}
|
||||
|
||||
for ((parent, children) in children) {
|
||||
for (child in children) {
|
||||
appendLine( " type_${parent.name} -> type_${child.name}")
|
||||
}
|
||||
}
|
||||
|
||||
for ((type, uses) in valueReferences) {
|
||||
for (use in uses) {
|
||||
appendLine(" type_${type.name} -> type_${use.name} [style=dotted]")
|
||||
}
|
||||
}
|
||||
|
||||
appendLine("}")
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun from(world: AstWorld): AstGraph {
|
||||
val graph = AstGraph()
|
||||
for (type in world.typeRegistry.types) {
|
||||
graph.add(type)
|
||||
}
|
||||
return graph
|
||||
}
|
||||
}
|
||||
}
|
@ -1,22 +1,38 @@
|
||||
package gay.pizza.pork.buildext.ast
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
|
||||
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.readText
|
||||
|
||||
class AstWorld {
|
||||
val typeRegistry: AstTypeRegistry = AstTypeRegistry()
|
||||
|
||||
fun typesInDependencyOrder(): List<AstType> {
|
||||
val typesInNameOrder = typeRegistry.types.sortedBy { it.name }
|
||||
val typesInDependencyOrder = mutableListOf<AstType>()
|
||||
for (type in typesInNameOrder) {
|
||||
|
||||
fun add(type: AstType, resolving: MutableSet<AstType>) {
|
||||
if (resolving.contains(type)) {
|
||||
val cyclePath = resolving.joinToString(" -> ") { it.name }
|
||||
throw RuntimeException("Dependency cycle detected: $cyclePath")
|
||||
}
|
||||
resolving.add(type)
|
||||
|
||||
if (type.parent != null) {
|
||||
if (!typesInDependencyOrder.contains(type.parent)) {
|
||||
typesInDependencyOrder.add(type.parent!!)
|
||||
}
|
||||
add(type.parent!!, resolving)
|
||||
}
|
||||
|
||||
if (!typesInDependencyOrder.contains(type)) {
|
||||
typesInDependencyOrder.add(type)
|
||||
}
|
||||
}
|
||||
|
||||
for (type in typesInNameOrder) {
|
||||
add(type, mutableSetOf())
|
||||
}
|
||||
|
||||
return typesInDependencyOrder
|
||||
}
|
||||
|
||||
@ -55,5 +71,13 @@ class AstWorld {
|
||||
}
|
||||
return world
|
||||
}
|
||||
|
||||
fun read(path: Path): AstWorld {
|
||||
val astYamlText = path.readText()
|
||||
val mapper = ObjectMapper(YAMLFactory())
|
||||
mapper.registerModules(KotlinModule.Builder().build())
|
||||
val astDescription = mapper.readValue(astYamlText, AstDescription::class.java)
|
||||
return build(astDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,9 +5,10 @@ import kotlin.io.path.Path
|
||||
object RunCodegenIde {
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) {
|
||||
val world = AstWorld.read(Path("src/main/ast/pork.yml"))
|
||||
AstCodegen.run(
|
||||
pkg = "gay.pizza.pork.ast",
|
||||
astDescriptionFile = Path("src/main/ast/pork.yml"),
|
||||
world = world,
|
||||
outputDirectory = Path("src/main/kotlin/gay/pizza/pork/ast")
|
||||
)
|
||||
}
|
||||
|
Reference in New Issue
Block a user