ast: generate graph

This commit is contained in:
Alex Zenla 2023-09-10 02:34:02 -04:00
parent 0bc3128b9d
commit f433ba2776
Signed by: alex
GPG Key ID: C0780728420EBFE5
7 changed files with 188 additions and 15 deletions

1
.gitignore vendored
View File

@ -6,3 +6,4 @@ out/
/work
/kotlin-js-store
/.fleet/*
.DS_Store

View 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]
}

View File

@ -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())
}
}

View File

@ -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()
}

View File

@ -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
}
}
}

View File

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

View File

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