Skip to content

Commit

Permalink
ast: generate graph
Browse files Browse the repository at this point in the history
  • Loading branch information
azenla committed Sep 10, 2023
1 parent 0bc3128 commit f433ba2
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 15 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ out/
/work
/kotlin-js-store
/.fleet/*
.DS_Store
81 changes: 81 additions & 0 deletions ast/src/main/graph/types.dot
Original file line number Diff line number Diff line change
@@ -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]
}
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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())
}
}
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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()
}
Expand Down
58 changes: 58 additions & 0 deletions buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstGraph.kt
Original file line number Diff line number Diff line change
@@ -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
}
}
}
32 changes: 28 additions & 4 deletions buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstWorld.kt
Original file line number Diff line number Diff line change
@@ -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
}

Expand Down Expand Up @@ -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)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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")
)
}
Expand Down

0 comments on commit f433ba2

Please sign in to comment.