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

parsley.debugger.internal.DebugContext.scala Maven / Gradle / Ivy

/*
 * Copyright 2020 Parsley Contributors 
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
package parsley.debugger.internal

import scala.collection.mutable

import org.typelevel.scalaccompat.annotation.unused
import parsley.XAssert
import parsley.debugger.ParseAttempt
import parsley.debugger.combinator.defaultRules
import parsley.internal.deepembedding.frontend.LazyParsley

// Class used to hold details about a parser being debugged.
// This is normally held as a value inside an implicit variable.
// Anything caught by the toStringRules will have a parse result of that type toString-ed for memory
// efficiency.
private [parsley] class DebugContext(val toStringRules: Seq[Any => Boolean] = defaultRules) {
    // Create a new dummy root of the tree that will act as filler for the rest of the tree to build
    // off of (as there is no "nil" representation for the tree... other than null, which should be
    // avoided in Scala wherever possible).
    private val dummyRoot: TransientDebugTree =
        TransientDebugTree("ROOT", "ROOT", "NIL")

    // Context's checkStack vanished.
    // Migrating it here for futureproofing.
    private val checkStack = mutable.ListBuffer.empty[(Int, Int, Int)]

    def pushPos(offset: Int, line: Int, col: Int): Unit = {
        checkStack.prepend((offset, line, col))
    }

    def popPos(): (Int, Int, Int) = {
        checkStack.remove(0)
    }

    // Tracks where we are in the parser callstack.
    private val builderStack = mutable.ListBuffer[TransientDebugTree](dummyRoot)

    // Get the final DebugTree from this context.
    def getFinalTree: TransientDebugTree = {
        // The root tree exists only as a placeholder for the rest of the debug tree to build off of.
        // If it has no children, that means the debug tree was not built to begin with.
        // If it was multiple children, somehow the debugger has popped too many tree nodes off the stack.
        val ch = builderStack.head.children
        XAssert.assert(!(ch.size < 1), s"The root tree has somehow lost its only child. (${ch.size})")
        XAssert.assert(!(ch.size > 1), s"The root tree has somehow gained multiple children. (${ch.size})")

        // This should never fail.
        ch.collectFirst { case (_, x) => x }.get
    }

    // Add an attempt of parsing at the current stack point.
    def addParseAttempt(attempt: ParseAttempt): Unit = {
        builderStack.head.parse = Some(attempt)

        // This child has consumed part of the parent's input. Add a hole.
        if (attempt.fromOffset != attempt.toOffset && builderStack(1) != dummyRoot) {
            val uuid = builderStack(1).augmentInput(attempt.fromOffset, attempt.toOffset)
            builderStack.head.cNumber = Some(uuid)
        }
    }

    // Reset this context back to zero.
    def reset(): Unit = {
        // Clear anything hanging off the dummy root.
        dummyRoot.children.clear()
        checkStack.clear()

        // The builder stack always starts with just the dummy root, so children can hang off it while building.
        builderStack.clear()
        builderStack.append(dummyRoot)
    }

    // Unique parser IDs.
    private var uid = -1L
    private def nextUid(): Long = {
        uid += 1L
        uid
    }

    // Push a new parser onto the parser callstack.
    def push(fullInput: String, parser: LazyParsley[_], optName: Option[String]): Unit = {
        val newTree = TransientDebugTree(fullInput = fullInput)
        newTree.name = Rename(optName, parser)
        newTree.internal = Rename.partial(parser)

        builderStack.head.children(newTree.name + "-#" + nextUid()) = newTree
        builderStack.prepend(newTree)
    }

    // Pop a parser off the parser callstack.
    def pop(): Unit = {
        // $COVERAGE-OFF$
        assert(builderStack.nonEmpty, "Parser stack underflow on pop.")
        // $COVERAGE-ON$
        // Remove first parser off stack, as if returning from that parser.
        val _ = builderStack.remove(0).applyInputAugments(): @unused
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy