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

parsley.internal.machine.instructions.IterativeInstrs.scala Maven / Gradle / Ivy

The newest version!
package parsley.internal.machine.instructions

import parsley.internal.deepembedding
import parsley.internal.machine.{Context, Good}
import parsley.internal.machine.stacks.{HandlerStack, Stack}, Stack.StackExt

import scala.collection.mutable.ListBuffer

private [internal] final class Many(var label: Int) extends InstrWithLabel with Stateful {
    private [this] val acc: ListBuffer[Any] = ListBuffer.empty
    override def apply(ctx: Context): Unit = {
        if (ctx.status eq Good) {
            acc += ctx.stack.upop()
            ctx.updateCheckOffsetAndHints()
            ctx.pc = label
        }
        // If the head of input stack is not the same size as the head of check stack, we fail to next handler
        else {
            ctx.catchNoConsumed {
                ctx.addErrorToHintsAndPop()
                ctx.pushAndContinue(acc.toList)
            }
            acc.clear()
        }
    }
    // $COVERAGE-OFF$
    override def toString: String = s"Many($label)"
    // $COVERAGE-ON$
    override def copy: Many = new Many(label)
}
private [internal] final class SkipMany(var label: Int) extends InstrWithLabel {
    override def apply(ctx: Context): Unit = {
        if (ctx.status eq Good) {
            ctx.stack.pop_()
            ctx.updateCheckOffsetAndHints()
            ctx.pc = label
        }
        // If the head of input stack is not the same size as the head of check stack, we fail to next handler
        else ctx.catchNoConsumed {
            ctx.addErrorToHintsAndPop()
            ctx.pushAndContinue(())
        }
    }
    // $COVERAGE-OFF$
    override def toString: String = s"SkipMany($label)"
    // $COVERAGE-ON$
}

private [internal] final class ChainPost(var label: Int) extends InstrWithLabel with Stateful {
    private [this] var acc: Any = _
    override def apply(ctx: Context): Unit = {
        if (ctx.status eq Good) {
            // When acc is null, we are entering the instruction for the first time, a p will be on the stack
            if (acc == null) {
                // after this point, the inputCheck will roll back one too many items on the stack, because this item
                // was consumed. It should be adjusted
                val op = ctx.stack.upop()
                acc = ctx.stack.upeek
                ctx.stack.exchange(op)
                ctx.handlers.stacksz -= 1
            }
            acc = ctx.stack.pop[Any => Any]()(acc)
            ctx.updateCheckOffsetAndHints()
            ctx.pc = label
        }
        // If the head of input stack is not the same size as the head of check stack, we fail to next handler
        else {
            ctx.catchNoConsumed {
                ctx.addErrorToHintsAndPop()
                // When acc is null, we have entered for first time but the op failed, so the result is already on the stack
                if (acc != null) ctx.stack.push(acc)
                ctx.inc()
            }
            acc = null
        }
    }
    // $COVERAGE-OFF$
    override def toString: String = s"ChainPost($label)"
    // $COVERAGE-ON$
    override def copy: ChainPost = new ChainPost(label)
}

private [internal] final class ChainPre(var label: Int) extends InstrWithLabel with Stateful {
    private var acc: Any => Any = _
    override def apply(ctx: Context): Unit = {
        if (ctx.status eq Good) {
            // If acc is null we are entering the instruction, so nothing to compose, this saves on an identity call
            acc = if (acc == null) ctx.stack.pop[Any => Any]()
                  // We perform the acc after the tos function; the tos function is "closer" to the final p
                  else ctx.stack.pop[Any => Any]().andThen(acc)
            ctx.updateCheckOffsetAndHints()
            ctx.pc = label
        }
        // If the head of input stack is not the same size as the head of check stack, we fail to next handler
        else {
            ctx.catchNoConsumed {
                ctx.addErrorToHintsAndPop()
                ctx.pushAndContinue(if (acc == null) identity[Any] _ else acc)
            }
            acc = null
        }
    }
    // $COVERAGE-OFF$
    override def toString: String = s"ChainPre($label)"
    // $COVERAGE-ON$
    override def copy: ChainPre = new ChainPre(label)
}
private [internal] final class Chainl(var label: Int) extends InstrWithLabel with Stateful {
    private [this] var acc: Any = _
    override def apply(ctx: Context): Unit = {
        if (ctx.status eq Good) {
            val y = ctx.stack.upop()
            val op = ctx.stack.pop[(Any, Any) => Any]()
            // When acc is null, we are entering the instruction for the first time, a p will be on the stack
            if (acc == null) {
                // after this point, the inputCheck will roll back one too many items on the stack, because this item
                // was consumed. It should be adjusted
                acc = op(ctx.stack.upop(), y)
                ctx.handlers.stacksz -= 1
            }
            else acc = op(acc, y)
            ctx.updateCheckOffsetAndHints()
            ctx.pc = label
        }
        // If the head of input stack is not the same size as the head of check stack, we fail to next handler
        else {
            ctx.catchNoConsumed {
                ctx.addErrorToHintsAndPop()
                // if acc is null this is first entry, p already on the stack!
                if (acc != null) ctx.pushAndContinue(acc)
                // but p does need to be wrapped
                else ctx.exchangeAndContinue(ctx.stack.upeek)
            }
            acc = null
        }
    }
    // $COVERAGE-OFF$
    override def toString: String = s"Chainl($label)"
    // $COVERAGE-ON$
    override def copy: Chainl = new Chainl(label)
}

private [instructions] sealed trait DualHandler {
    final protected def checkForFirstHandlerAndPop(ctx: Context, otherwise: =>Unit)(action: =>Unit) = {
        if (!ctx.handlers.isEmpty && ctx.handlers.pc == ctx.pc) {
            ctx.handlers = ctx.handlers.tail
            action
        }
        else otherwise
        ctx.checkStack = ctx.checkStack.tail
    }
    final protected def popSecondHandlerAndJump(ctx: Context, label: Int) = {
        ctx.handlers = ctx.handlers.tail
        ctx.checkStack = ctx.checkStack.tail
        ctx.updateCheckOffsetAndHints()
        ctx.pc = label
    }
}
private [internal] final class Chainr[A, B](var label: Int, _wrap: A => B) extends InstrWithLabel with DualHandler with Stateful{
    private [this] val wrap: Any => B = _wrap.asInstanceOf[Any => B]
    private [this] var acc: Any => Any = _
    override def apply(ctx: Context): Unit = {
        if (ctx.status eq Good) {
            val f = ctx.stack.pop[(Any, Any) => Any]()
            val x = ctx.stack.upop()
            // If acc is null we are entering the instruction, so nothing to compose, this saves on an identity call
            if (acc == null) acc = (y: Any) => f(x, y)
            // We perform the acc after the tos function; the tos function is "closer" to the final p
            else {
                // This must be bound here to avoid late binding issues
                val acc_ = acc
                acc = (y: Any) => acc_(f(x, y))
            }
            popSecondHandlerAndJump(ctx, label)
        }
        // If the head of input stack is not the same size as the head of check stack, we fail to next handler
        else {
            // presence of first handler indicates p succeeded and op didn't
            checkForFirstHandlerAndPop(ctx, ctx.fail()) {
                ctx.catchNoConsumed {
                    ctx.addErrorToHintsAndPop()
                    ctx.exchangeAndContinue(if (acc != null) acc(wrap(ctx.stack.upeek)) else wrap(ctx.stack.upeek))
                }
            }
            acc = null
        }
    }
    // $COVERAGE-OFF$
    override def toString: String = s"Chainr($label)"
    // $COVERAGE-ON$
    override def copy: Chainr[A, B] = new Chainr(label, wrap)
}

private [internal] final class SepEndBy1(var label: Int) extends InstrWithLabel with DualHandler with Stateful {
    private [this] val acc: ListBuffer[Any] = ListBuffer.empty
    override def apply(ctx: Context): Unit = {
        if (ctx.status eq Good) {
            ctx.stack.pop_()
            acc += ctx.stack.upop()
            popSecondHandlerAndJump(ctx, label)
        }
        else {
            val check = ctx.checkStack.offset
            // presence of first handler indicates p succeeded and sep didn't
            checkForFirstHandlerAndPop(ctx, ()) {
                acc += ctx.stack.upop()
                ctx.checkStack = ctx.checkStack.tail
            }
            if (ctx.offset != check || acc.isEmpty) ctx.fail()
            else {
                ctx.addErrorToHintsAndPop()
                ctx.status = Good
                ctx.pushAndContinue(acc.toList)
            }
            acc.clear()
        }
    }
    // $COVERAGE-OFF$
    override def toString: String = s"SepEndBy1($label)"
    // $COVERAGE-ON$
    override def copy: SepEndBy1 = new SepEndBy1(label)
}

private [internal] final class ManyUntil(var label: Int) extends InstrWithLabel with Stateful {
    private [this] val acc: ListBuffer[Any] = ListBuffer.empty
    override def apply(ctx: Context): Unit = {
        if (ctx.status eq Good) {
            val x = ctx.stack.upop()
            if (x == deepembedding.ManyUntil.Stop) {
                ctx.pushAndContinue(acc.toList)
                acc.clear()
                ctx.handlers = ctx.handlers.tail
            }
            else {
                acc += x
                ctx.pc = label
            }
        }
        // ManyUntil is a fallthrough handler, it must be visited during failure, but does nothing to the external state
        else { acc.clear(); ctx.fail() }
    }
    // $COVERAGE-OFF$
    override def toString: String = s"ManyUntil($label)"
    // $COVERAGE-ON$
    override def copy: ManyUntil = new ManyUntil(label)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy