mirror of
https://github.com/GayPizzaSpecifications/pork.git
synced 2025-08-03 05:10:55 +00:00
parser: use ast user data to store attribution
This commit is contained in:
parent
a07e0fe672
commit
b64c7fb259
@ -8,5 +8,6 @@ import kotlinx.serialization.Serializable
|
|||||||
@SerialName("definition")
|
@SerialName("definition")
|
||||||
sealed class Definition : Node() {
|
sealed class Definition : Node() {
|
||||||
abstract val symbol: Symbol
|
abstract val symbol: Symbol
|
||||||
|
|
||||||
abstract val modifiers: DefinitionModifiers
|
abstract val modifiers: DefinitionModifiers
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// GENERATED CODE FROM PORK AST CODEGEN
|
// GENERATED CODE FROM PORK AST CODEGEN
|
||||||
package gay.pizza.pork.ast
|
package gay.pizza.pork.ast
|
||||||
|
|
||||||
|
import kotlinx.serialization.Transient
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@ -9,6 +10,9 @@ import kotlinx.serialization.Serializable
|
|||||||
sealed class Node {
|
sealed class Node {
|
||||||
abstract val type: NodeType
|
abstract val type: NodeType
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
var data: Any? = null
|
||||||
|
|
||||||
open fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
open fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||||
emptyList()
|
emptyList()
|
||||||
|
|
||||||
|
6
ast/src/main/kotlin/gay/pizza/pork/ast/NodeExtensions.kt
Normal file
6
ast/src/main/kotlin/gay/pizza/pork/ast/NodeExtensions.kt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// GENERATED CODE FROM PORK AST CODEGEN
|
||||||
|
package gay.pizza.pork.ast
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
fun <P> Node.data(): P? =
|
||||||
|
data as? P?
|
@ -18,6 +18,7 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
|||||||
for (type in world.typeRegistry.types) {
|
for (type in world.typeRegistry.types) {
|
||||||
writeAstType(type)
|
writeAstType(type)
|
||||||
}
|
}
|
||||||
|
writeNodeExtensions()
|
||||||
writeNodeType()
|
writeNodeType()
|
||||||
writeNodeVisitors()
|
writeNodeVisitors()
|
||||||
writeNodeCoalescer()
|
writeNodeCoalescer()
|
||||||
@ -124,7 +125,6 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
|||||||
visitAllFunction.body.add(
|
visitAllFunction.body.add(
|
||||||
"nodeLists.asSequence().flatten().filterNotNull().map { visit(it) }.toList()")
|
"nodeLists.asSequence().flatten().filterNotNull().map { visit(it) }.toList()")
|
||||||
visitorExtensionSet.functions.add(visitAllFunction)
|
visitorExtensionSet.functions.add(visitAllFunction)
|
||||||
|
|
||||||
write("NodeVisitorExtensions.kt", KotlinWriter(visitorExtensionSet))
|
write("NodeVisitorExtensions.kt", KotlinWriter(visitorExtensionSet))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,12 +190,22 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (role == AstTypeRole.RootNode) {
|
if (role == AstTypeRole.RootNode) {
|
||||||
|
kotlinClassLike.imports.add("kotlinx.serialization.Transient")
|
||||||
val typeMember = KotlinMember(
|
val typeMember = KotlinMember(
|
||||||
"type",
|
"type",
|
||||||
"NodeType",
|
"NodeType",
|
||||||
abstract = true
|
abstract = true
|
||||||
)
|
)
|
||||||
kotlinClassLike.members.add(typeMember)
|
kotlinClassLike.members.add(typeMember)
|
||||||
|
val dataMember = KotlinMember(
|
||||||
|
"data",
|
||||||
|
"Any?",
|
||||||
|
value = "null",
|
||||||
|
mutable = true,
|
||||||
|
notInsideConstructor = true,
|
||||||
|
annotations = mutableListOf("@Transient")
|
||||||
|
)
|
||||||
|
kotlinClassLike.members.add(dataMember)
|
||||||
|
|
||||||
val abstractVisitChildrenFunction = KotlinFunction(
|
val abstractVisitChildrenFunction = KotlinFunction(
|
||||||
"visitChildren",
|
"visitChildren",
|
||||||
@ -389,6 +399,21 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
|||||||
write("${type.name}.kt", KotlinWriter(kotlinClassLike))
|
write("${type.name}.kt", KotlinWriter(kotlinClassLike))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun writeNodeExtensions() {
|
||||||
|
val nodeExtensionSet = KotlinFunctionSet(pkg)
|
||||||
|
val dataFunction = KotlinFunction(
|
||||||
|
"data",
|
||||||
|
typeParameters = mutableListOf("P"),
|
||||||
|
extensionOf = "Node",
|
||||||
|
returnType = "P?",
|
||||||
|
isImmediateExpression = true,
|
||||||
|
annotations = mutableListOf("""@Suppress("UNCHECKED_CAST")""")
|
||||||
|
)
|
||||||
|
dataFunction.body.add("data as? P?")
|
||||||
|
nodeExtensionSet.functions.add(dataFunction)
|
||||||
|
write("NodeExtensions.kt", KotlinWriter(nodeExtensionSet))
|
||||||
|
}
|
||||||
|
|
||||||
private fun toKotlinType(typeRef: AstTypeRef): String {
|
private fun toKotlinType(typeRef: AstTypeRef): String {
|
||||||
val baseType = typeRef.type?.name ?: typeRef.primitive?.id
|
val baseType = typeRef.type?.name ?: typeRef.primitive?.id
|
||||||
?: throw RuntimeException("Unable to determine base type.")
|
?: throw RuntimeException("Unable to determine base type.")
|
||||||
|
@ -6,5 +6,9 @@ class KotlinMember(
|
|||||||
var abstract: Boolean = false,
|
var abstract: Boolean = false,
|
||||||
var overridden: Boolean = false,
|
var overridden: Boolean = false,
|
||||||
var value: String? = null,
|
var value: String? = null,
|
||||||
var mutable: Boolean = false
|
var mutable: Boolean = false,
|
||||||
|
var private: Boolean = false,
|
||||||
|
var protected: Boolean = false,
|
||||||
|
var notInsideConstructor: Boolean = false,
|
||||||
|
var annotations: MutableList<String> = mutableListOf()
|
||||||
)
|
)
|
||||||
|
@ -15,7 +15,7 @@ class KotlinWriter() {
|
|||||||
}
|
}
|
||||||
writeClassLike(classType, kotlinClass)
|
writeClassLike(classType, kotlinClass)
|
||||||
val members = kotlinClass.members.filter {
|
val members = kotlinClass.members.filter {
|
||||||
it.abstract || (it.overridden && it.value != null)
|
it.abstract || (it.overridden && it.value != null) || it.notInsideConstructor
|
||||||
}
|
}
|
||||||
if (members.isEmpty() && kotlinClass.functions.isEmpty()) {
|
if (members.isEmpty() && kotlinClass.functions.isEmpty()) {
|
||||||
appendLine()
|
appendLine()
|
||||||
@ -23,13 +23,23 @@ class KotlinWriter() {
|
|||||||
appendLine(" {")
|
appendLine(" {")
|
||||||
}
|
}
|
||||||
|
|
||||||
for (member in members) {
|
for ((index, member) in members.withIndex()) {
|
||||||
val form = if (member.mutable) "var" else "val"
|
for (annotation in member.annotations) {
|
||||||
|
appendLine(" $annotation")
|
||||||
|
}
|
||||||
|
|
||||||
|
val privacy = when {
|
||||||
|
member.private -> "private "
|
||||||
|
member.protected -> "protected "
|
||||||
|
else -> ""
|
||||||
|
}
|
||||||
|
val form = if (member.mutable) "${privacy}var" else "${privacy}val"
|
||||||
if (member.abstract) {
|
if (member.abstract) {
|
||||||
appendLine(" abstract $form ${member.name}: ${member.type}")
|
appendLine(" abstract $form ${member.name}: ${member.type}")
|
||||||
} else {
|
} else {
|
||||||
|
append(" ")
|
||||||
if (member.overridden) {
|
if (member.overridden) {
|
||||||
append(" override ")
|
append("override ")
|
||||||
}
|
}
|
||||||
append("$form ${member.name}: ${member.type}")
|
append("$form ${member.name}: ${member.type}")
|
||||||
if (member.value != null) {
|
if (member.value != null) {
|
||||||
@ -38,6 +48,10 @@ class KotlinWriter() {
|
|||||||
}
|
}
|
||||||
appendLine()
|
appendLine()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (index != members.size - 1) {
|
||||||
|
appendLine()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (members.isNotEmpty() && kotlinClass.functions.isNotEmpty()) {
|
if (members.isNotEmpty() && kotlinClass.functions.isNotEmpty()) {
|
||||||
@ -109,7 +123,7 @@ class KotlinWriter() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val contructedMembers = kotlinClass.members.filter {
|
val contructedMembers = kotlinClass.members.filter {
|
||||||
!it.abstract && !(it.overridden && it.value != null)
|
!it.abstract && !(it.overridden && it.value != null) && !it.notInsideConstructor
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contructedMembers.isNotEmpty()) {
|
if (contructedMembers.isNotEmpty()) {
|
||||||
|
@ -2,6 +2,7 @@ package gay.pizza.pork.parser
|
|||||||
|
|
||||||
import gay.pizza.pork.ast.*
|
import gay.pizza.pork.ast.*
|
||||||
|
|
||||||
|
@Suppress("SameParameterValue")
|
||||||
class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
|
class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
|
||||||
private val unsanitizedSource = source
|
private val unsanitizedSource = source
|
||||||
|
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
package gay.pizza.pork.parser
|
||||||
|
|
||||||
|
import gay.pizza.pork.ast.Node
|
||||||
|
import gay.pizza.pork.ast.NodeCoalescer
|
||||||
|
import gay.pizza.pork.ast.data
|
||||||
|
import gay.pizza.pork.ast.visit
|
||||||
|
|
||||||
|
data class ParserAttributes(val tokens: List<Token>) {
|
||||||
|
companion object {
|
||||||
|
fun recallAllTokens(node: Node): List<Token> {
|
||||||
|
val all = mutableListOf<Token>()
|
||||||
|
val coalescer = NodeCoalescer { item ->
|
||||||
|
val attributes = item.data<ParserAttributes>()
|
||||||
|
if (attributes != null) {
|
||||||
|
all.addAll(attributes.tokens)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
coalescer.visit(node)
|
||||||
|
all.sortBy { it.start }
|
||||||
|
return all
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package gay.pizza.pork.parser
|
||||||
|
|
||||||
|
import gay.pizza.pork.ast.Node
|
||||||
|
import gay.pizza.pork.ast.data
|
||||||
|
|
||||||
|
class ParserNodeAttribution : NodeAttribution {
|
||||||
|
private val stack = mutableListOf<MutableList<Token>>()
|
||||||
|
private var current: MutableList<Token>? = null
|
||||||
|
|
||||||
|
override fun enter() {
|
||||||
|
val store = mutableListOf<Token>()
|
||||||
|
current = store
|
||||||
|
stack.add(store)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun push(token: Token) {
|
||||||
|
val store = current ?: throw RuntimeException("enter() not called!")
|
||||||
|
store.add(token)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <T : Node> adopt(node: T) {
|
||||||
|
val attributes = node.data<ParserAttributes>()
|
||||||
|
if (attributes != null) {
|
||||||
|
for (token in attributes.tokens) {
|
||||||
|
push(token)
|
||||||
|
}
|
||||||
|
node.data = ParserAttributes(emptyList())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <T: Node> exit(node: T): T {
|
||||||
|
val store = stack.removeLast()
|
||||||
|
current = stack.lastOrNull()
|
||||||
|
node.data = ParserAttributes(store)
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
}
|
@ -1,54 +0,0 @@
|
|||||||
package gay.pizza.pork.parser
|
|
||||||
|
|
||||||
import gay.pizza.pork.ast.NodeCoalescer
|
|
||||||
import gay.pizza.pork.ast.Node
|
|
||||||
import gay.pizza.pork.ast.visit
|
|
||||||
import java.util.IdentityHashMap
|
|
||||||
|
|
||||||
class TokenNodeAttribution : NodeAttribution {
|
|
||||||
val nodes: MutableMap<Node, List<Token>> = IdentityHashMap()
|
|
||||||
|
|
||||||
private val stack = mutableListOf<MutableList<Token>>()
|
|
||||||
private var current: MutableList<Token>? = null
|
|
||||||
|
|
||||||
override fun enter() {
|
|
||||||
val store = mutableListOf<Token>()
|
|
||||||
current = store
|
|
||||||
stack.add(store)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun push(token: Token) {
|
|
||||||
val store = current ?: throw RuntimeException("enter() not called!")
|
|
||||||
store.add(token)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun <T : Node> adopt(node: T) {
|
|
||||||
val tokens = nodes.remove(node)
|
|
||||||
if (tokens != null) {
|
|
||||||
for (token in tokens) {
|
|
||||||
push(token)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun <T: Node> exit(node: T): T {
|
|
||||||
val store = stack.removeLast()
|
|
||||||
nodes[node] = store
|
|
||||||
current = stack.lastOrNull()
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
|
|
||||||
fun tokensOf(node: Node): List<Token>? = nodes[node]
|
|
||||||
|
|
||||||
fun assembleTokens(node: Node): List<Token> {
|
|
||||||
val allTokens = mutableListOf<Token>()
|
|
||||||
val coalescer = NodeCoalescer { item ->
|
|
||||||
val tokens = tokensOf(item)
|
|
||||||
if (tokens != null) {
|
|
||||||
allTokens.addAll(tokens)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
coalescer.visit(node)
|
|
||||||
return allTokens.asSequence().distinct().sortedBy { it.start }.toList()
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,23 +3,26 @@ package gay.pizza.pork.tool
|
|||||||
import com.github.ajalt.clikt.core.CliktCommand
|
import com.github.ajalt.clikt.core.CliktCommand
|
||||||
import com.github.ajalt.clikt.parameters.arguments.argument
|
import com.github.ajalt.clikt.parameters.arguments.argument
|
||||||
import gay.pizza.dough.fs.PlatformFsProvider
|
import gay.pizza.dough.fs.PlatformFsProvider
|
||||||
|
import gay.pizza.pork.ast.Node
|
||||||
import gay.pizza.pork.ast.NodeCoalescer
|
import gay.pizza.pork.ast.NodeCoalescer
|
||||||
|
import gay.pizza.pork.ast.data
|
||||||
import gay.pizza.pork.ast.visit
|
import gay.pizza.pork.ast.visit
|
||||||
import gay.pizza.pork.minimal.FileTool
|
import gay.pizza.pork.minimal.FileTool
|
||||||
import gay.pizza.pork.parser.TokenNodeAttribution
|
import gay.pizza.pork.parser.ParserAttributes
|
||||||
|
import gay.pizza.pork.parser.ParserNodeAttribution
|
||||||
|
|
||||||
class AttributeCommand : CliktCommand(help = "Attribute AST", name = "attribute") {
|
class AttributeCommand : CliktCommand(help = "Attribute AST", name = "attribute") {
|
||||||
val path by argument("file")
|
val path by argument("file")
|
||||||
|
|
||||||
override fun run() {
|
override fun run() {
|
||||||
val tool = FileTool(PlatformFsProvider.resolve(path))
|
val tool = FileTool(PlatformFsProvider.resolve(path))
|
||||||
val attribution = TokenNodeAttribution()
|
val attribution = ParserNodeAttribution()
|
||||||
val compilationUnit = tool.parse(attribution)
|
val compilationUnit = tool.parse(attribution)
|
||||||
|
|
||||||
val coalescer = NodeCoalescer { node ->
|
val coalescer = NodeCoalescer { node ->
|
||||||
val tokens = attribution.assembleTokens(node)
|
val allTokens = ParserAttributes.recallAllTokens(node)
|
||||||
println("node ${node.type.name}")
|
println("node ${node.type.name}")
|
||||||
for (token in tokens) {
|
for (token in allTokens) {
|
||||||
println("token $token")
|
println("token $token")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user