All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.org.intellij.markdown.parser.TreeBuilder.kt Maven / Gradle / Ivy

There is a newer version: 0.7.3
Show newest version
package org.intellij.markdown.parser

import org.intellij.markdown.ast.ASTNode
import org.intellij.markdown.ast.ASTNodeBuilder
import org.intellij.markdown.lexer.Compat.assert
import org.intellij.markdown.lexer.Stack
import org.intellij.markdown.parser.sequentialparsers.SequentialParser

abstract class TreeBuilder(protected val nodeBuilder: ASTNodeBuilder) {

    fun buildTree(production: List): ASTNode {
        val events = constructEvents(production)
        val markersStack = Stack>>()

        assert(events.isNotEmpty()) { "nonsense" }
        assert(events.first().info == events.last().info) {
            "more than one root?\nfirst: ${events.first().info}\nlast: ${events.last().info}"
        }

        for (i in events.indices) {
            val event = events[i]

            flushEverythingBeforeEvent(event, if (markersStack.isEmpty()) null else markersStack.peek().second)


            if (event.isStart()) {
                markersStack.push(Pair(event, ArrayList()))
            } else {
                val currentNodeChildren = if (event.isEmpty()) {
                    ArrayList()
                } else {
                    val eventAndChildren = markersStack.pop()
                    assert(eventAndChildren.first.info == event.info) {
                        "Intersecting parsed nodes detected: ${eventAndChildren.first.info} vs ${event.info}"
                    }
                    eventAndChildren.second
                }
                val isTopmostNode = markersStack.isEmpty()

                val newNode = createASTNodeOnClosingEvent(event, currentNodeChildren, isTopmostNode)

                if (isTopmostNode) {
                    assert(i + 1 == events.size)
                    return newNode.astNode
                } else {
                    markersStack.peek().second.add(newNode)
                }
            }
        }

        throw AssertionError("markers stack should close some time thus would not be here!")
    }

    protected abstract fun createASTNodeOnClosingEvent(event: MyEvent, currentNodeChildren: List, isTopmostNode: Boolean): MyASTNodeWrapper

    protected abstract fun flushEverythingBeforeEvent(event: MyEvent, currentNodeChildren: MutableList?)

    private fun constructEvents(production: List): List {
        val events = ArrayList()
        for (index in production.indices) {
            val result = production[index]
            val startTokenId = result.range.first
            val endTokenId = result.range.last

            events.add(MyEvent(startTokenId, index, result))
            if (endTokenId != startTokenId) {
                events.add(MyEvent(endTokenId, index, result))
            }
        }
        events.sort()
        return events
    }


    protected class MyEvent(val position: Int,
                          val timeClosed: Int,
                          val info: SequentialParser.Node) : Comparable {

        fun isStart(): Boolean {
            return info.range.last != position
        }

        fun isEmpty(): Boolean {
            return info.range.first == info.range.last
        }

        override fun compareTo(other: MyEvent): Int {
            if (position != other.position) {
                return position - other.position
            }
            if (isStart() == other.isStart()) {
                val positionDiff = info.range.first + info.range.last - (other.info.range.first + other.info.range.last)
                if (positionDiff != 0) {
                    return -positionDiff
                }

                val timeDiff = timeClosed - other.timeClosed
                return if (isStart()) {
                    -timeDiff
                } else {
                    timeDiff
                }
            }
            return if (isStart()) 1 else -1
        }

        override fun toString(): String {
            return "${if (isStart()) "Open" else "Close"}: $position (${info})"
        }
    }

    protected class MyASTNodeWrapper(val astNode: ASTNode, val startTokenIndex: Int, val endTokenIndex: Int)

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy