mirror of
				https://github.com/GayPizzaSpecifications/pork.git
				synced 2025-11-03 17:39:38 +00:00 
			
		
		
		
	parser: use ast user data to store attribution
This commit is contained in:
		@ -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")
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user