mirror of
https://github.com/GayPizzaSpecifications/pork.git
synced 2025-08-02 21:00:56 +00:00
ast: generate graph
This commit is contained in:
parent
0bc3128b9d
commit
f433ba2776
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,3 +6,4 @@ out/
|
|||||||
/work
|
/work
|
||||||
/kotlin-js-store
|
/kotlin-js-store
|
||||||
/.fleet/*
|
/.fleet/*
|
||||||
|
.DS_Store
|
||||||
|
81
ast/src/main/graph/types.dot
Normal file
81
ast/src/main/graph/types.dot
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
digraph A {
|
||||||
|
type_Node [shape=box,label="Node"]
|
||||||
|
type_Expression [shape=box,label="Expression"]
|
||||||
|
type_Symbol [shape=box,label="Symbol"]
|
||||||
|
type_Declaration [shape=box,label="Declaration"]
|
||||||
|
type_Definition [shape=box,label="Definition"]
|
||||||
|
type_DefinitionModifiers [shape=box,label="DefinitionModifiers"]
|
||||||
|
type_Block [shape=box,label="Block"]
|
||||||
|
type_CompilationUnit [shape=box,label="CompilationUnit"]
|
||||||
|
type_LetAssignment [shape=box,label="LetAssignment"]
|
||||||
|
type_InfixOperator [shape=box,label="InfixOperator"]
|
||||||
|
type_InfixOperation [shape=box,label="InfixOperation"]
|
||||||
|
type_BooleanLiteral [shape=box,label="BooleanLiteral"]
|
||||||
|
type_FunctionCall [shape=box,label="FunctionCall"]
|
||||||
|
type_FunctionDefinition [shape=box,label="FunctionDefinition"]
|
||||||
|
type_If [shape=box,label="If"]
|
||||||
|
type_ImportDeclaration [shape=box,label="ImportDeclaration"]
|
||||||
|
type_IntegerLiteral [shape=box,label="IntegerLiteral"]
|
||||||
|
type_DoubleLiteral [shape=box,label="DoubleLiteral"]
|
||||||
|
type_ListLiteral [shape=box,label="ListLiteral"]
|
||||||
|
type_Parentheses [shape=box,label="Parentheses"]
|
||||||
|
type_PrefixOperator [shape=box,label="PrefixOperator"]
|
||||||
|
type_PrefixOperation [shape=box,label="PrefixOperation"]
|
||||||
|
type_StringLiteral [shape=box,label="StringLiteral"]
|
||||||
|
type_SymbolReference [shape=box,label="SymbolReference"]
|
||||||
|
type_While [shape=box,label="While"]
|
||||||
|
type_Break [shape=box,label="Break"]
|
||||||
|
type_Continue [shape=box,label="Continue"]
|
||||||
|
type_Native [shape=box,label="Native"]
|
||||||
|
type_Node -> type_Expression
|
||||||
|
type_Node -> type_Symbol
|
||||||
|
type_Node -> type_Declaration
|
||||||
|
type_Node -> type_Definition
|
||||||
|
type_Node -> type_Block
|
||||||
|
type_Node -> type_CompilationUnit
|
||||||
|
type_Node -> type_Native
|
||||||
|
type_Expression -> type_LetAssignment
|
||||||
|
type_Expression -> type_InfixOperation
|
||||||
|
type_Expression -> type_BooleanLiteral
|
||||||
|
type_Expression -> type_FunctionCall
|
||||||
|
type_Expression -> type_If
|
||||||
|
type_Expression -> type_IntegerLiteral
|
||||||
|
type_Expression -> type_DoubleLiteral
|
||||||
|
type_Expression -> type_ListLiteral
|
||||||
|
type_Expression -> type_Parentheses
|
||||||
|
type_Expression -> type_PrefixOperation
|
||||||
|
type_Expression -> type_StringLiteral
|
||||||
|
type_Expression -> type_SymbolReference
|
||||||
|
type_Expression -> type_While
|
||||||
|
type_Expression -> type_Break
|
||||||
|
type_Expression -> type_Continue
|
||||||
|
type_Definition -> type_FunctionDefinition
|
||||||
|
type_Declaration -> type_ImportDeclaration
|
||||||
|
type_Definition -> type_Symbol [style=dotted]
|
||||||
|
type_Definition -> type_DefinitionModifiers [style=dotted]
|
||||||
|
type_Block -> type_Expression [style=dotted]
|
||||||
|
type_CompilationUnit -> type_Declaration [style=dotted]
|
||||||
|
type_CompilationUnit -> type_Definition [style=dotted]
|
||||||
|
type_LetAssignment -> type_Symbol [style=dotted]
|
||||||
|
type_LetAssignment -> type_Expression [style=dotted]
|
||||||
|
type_InfixOperation -> type_Expression [style=dotted]
|
||||||
|
type_InfixOperation -> type_InfixOperator [style=dotted]
|
||||||
|
type_FunctionCall -> type_Symbol [style=dotted]
|
||||||
|
type_FunctionCall -> type_Expression [style=dotted]
|
||||||
|
type_FunctionDefinition -> type_DefinitionModifiers [style=dotted]
|
||||||
|
type_FunctionDefinition -> type_Symbol [style=dotted]
|
||||||
|
type_FunctionDefinition -> type_Block [style=dotted]
|
||||||
|
type_FunctionDefinition -> type_Native [style=dotted]
|
||||||
|
type_If -> type_Expression [style=dotted]
|
||||||
|
type_If -> type_Block [style=dotted]
|
||||||
|
type_ImportDeclaration -> type_Symbol [style=dotted]
|
||||||
|
type_ListLiteral -> type_Expression [style=dotted]
|
||||||
|
type_Parentheses -> type_Expression [style=dotted]
|
||||||
|
type_PrefixOperation -> type_PrefixOperator [style=dotted]
|
||||||
|
type_PrefixOperation -> type_Expression [style=dotted]
|
||||||
|
type_SymbolReference -> type_Symbol [style=dotted]
|
||||||
|
type_While -> type_Expression [style=dotted]
|
||||||
|
type_While -> type_Block [style=dotted]
|
||||||
|
type_Native -> type_Symbol [style=dotted]
|
||||||
|
type_Native -> type_StringLiteral [style=dotted]
|
||||||
|
}
|
@ -1,12 +1,18 @@
|
|||||||
package gay.pizza.pork.buildext
|
package gay.pizza.pork.buildext
|
||||||
|
|
||||||
import gay.pizza.pork.buildext.ast.AstCodegen
|
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.DefaultTask
|
||||||
import org.gradle.api.tasks.Input
|
import org.gradle.api.tasks.Input
|
||||||
import org.gradle.api.tasks.InputFile
|
import org.gradle.api.tasks.InputFile
|
||||||
import org.gradle.api.tasks.TaskAction
|
import org.gradle.api.tasks.TaskAction
|
||||||
import org.gradle.api.tasks.OutputDirectory
|
import org.gradle.api.tasks.OutputDirectory
|
||||||
|
import org.gradle.api.tasks.OutputFile
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import kotlin.io.path.createDirectories
|
||||||
|
import kotlin.io.path.deleteIfExists
|
||||||
|
import kotlin.io.path.writeText
|
||||||
|
|
||||||
open class GenerateAstCode : DefaultTask() {
|
open class GenerateAstCode : DefaultTask() {
|
||||||
init {
|
init {
|
||||||
@ -22,8 +28,18 @@ open class GenerateAstCode : DefaultTask() {
|
|||||||
@get:OutputDirectory
|
@get:OutputDirectory
|
||||||
var outputDirectory: File = project.file("src/main/kotlin/gay/pizza/pork/ast")
|
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
|
@TaskAction
|
||||||
fun generate() {
|
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")
|
@file:Suppress("ConstPropertyName")
|
||||||
package gay.pizza.pork.buildext.ast
|
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 gay.pizza.pork.buildext.codegen.*
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
@ -407,15 +404,10 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
|||||||
companion object {
|
companion object {
|
||||||
private const val enableVisitAnyInline = false
|
private const val enableVisitAnyInline = false
|
||||||
|
|
||||||
fun run(pkg: String, astDescriptionFile: Path, outputDirectory: Path) {
|
fun run(pkg: String, world: AstWorld, outputDirectory: Path) {
|
||||||
if (!outputDirectory.exists()) {
|
if (!outputDirectory.exists()) {
|
||||||
outputDirectory.createDirectories()
|
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)
|
val codegen = AstCodegen(pkg, outputDirectory, world)
|
||||||
codegen.generate()
|
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
|
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 {
|
class AstWorld {
|
||||||
val typeRegistry: AstTypeRegistry = AstTypeRegistry()
|
val typeRegistry: AstTypeRegistry = AstTypeRegistry()
|
||||||
|
|
||||||
fun typesInDependencyOrder(): List<AstType> {
|
fun typesInDependencyOrder(): List<AstType> {
|
||||||
val typesInNameOrder = typeRegistry.types.sortedBy { it.name }
|
val typesInNameOrder = typeRegistry.types.sortedBy { it.name }
|
||||||
val typesInDependencyOrder = mutableListOf<AstType>()
|
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 (type.parent != null) {
|
||||||
if (!typesInDependencyOrder.contains(type.parent)) {
|
add(type.parent!!, resolving)
|
||||||
typesInDependencyOrder.add(type.parent!!)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!typesInDependencyOrder.contains(type)) {
|
if (!typesInDependencyOrder.contains(type)) {
|
||||||
typesInDependencyOrder.add(type)
|
typesInDependencyOrder.add(type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (type in typesInNameOrder) {
|
||||||
|
add(type, mutableSetOf())
|
||||||
|
}
|
||||||
|
|
||||||
return typesInDependencyOrder
|
return typesInDependencyOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,5 +71,13 @@ class AstWorld {
|
|||||||
}
|
}
|
||||||
return world
|
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 {
|
object RunCodegenIde {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
|
val world = AstWorld.read(Path("src/main/ast/pork.yml"))
|
||||||
AstCodegen.run(
|
AstCodegen.run(
|
||||||
pkg = "gay.pizza.pork.ast",
|
pkg = "gay.pizza.pork.ast",
|
||||||
astDescriptionFile = Path("src/main/ast/pork.yml"),
|
world = world,
|
||||||
outputDirectory = Path("src/main/kotlin/gay/pizza/pork/ast")
|
outputDirectory = Path("src/main/kotlin/gay/pizza/pork/ast")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user