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

fastparse.parsers.Combinators.scala Maven / Gradle / Ivy

The newest version!
package scala.meta.internal.fastparse.parsers
// import acyclic.file
import scala.meta.internal.fastparse.utils.ReprOps
import scala.meta.internal.fastparse.core.Implicits
import Terminals._
import scala.meta.internal.fastparse.core.Parsed._
import scala.meta.internal.fastparse.core.Mutable
import scala.meta.internal.fastparse.core.{ParseCtx, Parsed, Parser, Precedence}

import scala.annotation.tailrec
import scala.collection.mutable.ArrayBuffer
/**
 * Parsers which are made up of other parsers,
 * adding to or combining their behavior
 */
object Combinators {

  /**
   * Captures the string parsed by the given parser [[p]].
   */
  case class Capturing[Elem, Repr](p: Parser[_, Elem, Repr])
                                  (implicit repr: ReprOps[Elem, Repr])
      extends Parser[Repr, Elem, Repr] {
    def parseRec(cfg: ParseCtx[Elem, Repr], index: Int) = {
      val oldCapturing = cfg.isCapturing
      cfg.isCapturing = true
      val res = p.parseRec(cfg, index)
      cfg.isCapturing = oldCapturing

      res match {
        case Mutable.Success(value0, index0, traceParsers0, cut0) =>
          success(
            cfg.success,
            cfg.input.slice(index, index0),
            index0,
            traceParsers0,
            cut0
          )
        case f: Mutable.Failure[Elem, Repr] => f
      }
    }
    override def toString = p.toString
  }

  /**
   * Wrap a parser in this if you don't want for it to show up in a stack trace
   */
  case class NoTrace[T, Elem, Repr](p: Parser[T, Elem, Repr])
                                   (implicit repr: ReprOps[Elem, Repr]) extends Parser[T, Elem, Repr]{
    def parseRec(cfg: ParseCtx[Elem, Repr], index: Int) = {
      p.parseRec(cfg, index) match {
        case s: Mutable.Success[_, Elem, Repr] =>
          s.traceParsers = Set.empty
          s
        case f: Mutable.Failure[Elem, Repr] =>
          f.traceParsers = Set.empty
          f
      }
    }
    override def toString = p.toString
  }

  /**
   * A wrapper that replaces target parser and its inner parsers in the stack trace.
   * Useful for providing more high-level error messages without going into details.
   * For example, "expected CharPred(...)..." can become "expected IDENT...".
   *
   * @param msg The message for the wrapper
   */
  case class Opaque[+T, Elem, Repr](p: Parser[T, Elem, Repr], msg: String)
                                   (implicit repr: ReprOps[Elem, Repr])
    extends Parser[T, Elem, Repr]{
    def parseRec(cfg: ParseCtx[Elem, Repr], index: Int) = {
      p.parseRec(cfg, index) match {
        case s: Mutable.Success[_, Elem, Repr] =>
          if (cfg.traceIndex != -1) s.traceParsers = Set(this)
          s
        case f: Mutable.Failure[Elem, Repr] =>
          f.index = index
          f.lastParser = this
          if (cfg.traceIndex != -1) f.traceParsers = Set(this)
          f
      }
    }
    override def toString = msg
  }

  /**
   *
   */
  case class NoCut[T, Elem, Repr](p: Parser[T, Elem, Repr])
                                 (implicit repr: ReprOps[Elem, Repr]) extends Parser[T, Elem, Repr]{
    def parseRec(cfg: ParseCtx[Elem, Repr], index: Int) = {
      val oldNoCut = cfg.isNoCut
      cfg.isNoCut = true
      val res = p.parseRec(cfg, index)
      cfg.isNoCut = oldNoCut

      res match {
        case s: Mutable.Success[T, Elem, Repr] =>
          s.cut = false
          s
        case f: Mutable.Failure[Elem, Repr] =>
          f.cut = false
          f
      }
    }
    override def toString = p.toString
  }
  /**
   * Wraps a parser and prints out the indices where it starts
   * and ends, together with its result
   */
  case class Logged[+T, Elem, Repr](p: Parser[T, Elem, Repr],
                                    msg: String, output: String => Unit)
                                   (implicit repr: ReprOps[Elem, Repr]) extends Parser[T, Elem, Repr]{
    def parseRec(cfg: ParseCtx[Elem, Repr], index: Int) = {
      if (cfg.logDepth == -1 || cfg.traceIndex != -1) p.parseRec(cfg, index)
      else {
        val indent = "  " * cfg.logDepth
        output(s"$indent+$msg:${repr.prettyIndex(cfg.input, index)}")
        val depth = cfg.logDepth
        cfg.logDepth += 1
        val res = p.parseRec(cfg, index)
        cfg.logDepth = depth
        val strRes = res match{
          case s: Mutable.Success[T, Elem, Repr] =>
            s"Success(${repr.prettyIndex(cfg.input, s.index)}${if (s.cut) ", cut" else ""})"
          case f: Mutable.Failure[Elem, Repr] =>
            val stack = Failure.filterFullStack(f.fullStack)
            val trace = Failure.formatStackTrace(
              stack.reverse,
              f.input,
              index,
              Failure.formatParser(f.lastParser, f.input, f.index)
            )
            s"Failure($trace${if (f.cut) ", cut" else ""})"
        }
        output(s"$indent-$msg:${repr.prettyIndex(cfg.input, index)}:$strRes")
        res
      }
    }
    override def toString = p.toString + ".log()"
  }


  /**
   * A top-level, named parser. Lazily evaluates the wrapped parser
   * [[p]] only when `parse` is called to allow for circular
   * dependencies between parsers.
   */
  case class Rule[+T, Elem, Repr](name: String, p: () => Parser[T, Elem, Repr])
                                 (implicit repr: ReprOps[Elem, Repr])
    extends Parser[T, Elem, Repr]{
    private[this] lazy val pCached = p()

    def parseRec(cfg: ParseCtx[Elem, Repr], index: Int) = {

      if (cfg.instrument == null) {
        pCached.parseRec(cfg, index) match{
          case f: Mutable.Failure[Elem, Repr] => failMore(f, index, cfg.logDepth)
          case s: Mutable.Success[T, Elem, Repr] => s
        }
      } else {
        lazy val res = pCached.parseRec(cfg, index) match{
          case f: Mutable.Failure[Elem, Repr] => failMore(f, index, cfg.logDepth)
          case s: Mutable.Success[T, Elem, Repr] => s
        }
        cfg.instrument(this, index, () => res.toResult)
        res
      }
    }
    override def toString = name
    override def shortTraced = true
  }

  /**
   * Wraps another parser, succeeding/failing identically
   * but consuming no input
   */
  case class Lookahead[Elem, Repr](p: Parser[_, Elem, Repr])
                                  (implicit repr: ReprOps[Elem, Repr]) extends Parser[Unit, Elem, Repr]{
    def parseRec(cfg: ParseCtx[Elem, Repr], index: Int) = {
      val oldNoCut = cfg.isNoCut
      cfg.isNoCut = true
      val res = p.parseRec(cfg, index)
      cfg.isNoCut = oldNoCut

      res match{
        case s: Mutable.Success[_, Elem, Repr] =>
          s.cut = false
          success(cfg.success, (), index, s.traceParsers, false)
        case f: Mutable.Failure[Elem, Repr] =>
          f.cut = false
          failMore(f, index, cfg.logDepth)
      }
    }
    override def toString = s"&($p)"
  }
  /**
   * Wraps another parser, succeeding it it fails and failing
   * if it succeeds. Neither case consumes any input
   */
  case class Not[Elem, Repr](p: Parser[_, Elem, Repr])
                            (implicit repr: ReprOps[Elem, Repr]) extends Parser[Unit, Elem, Repr]{
    def parseRec(cfg: ParseCtx[Elem, Repr], index: Int) = {
      val oldNoCut = cfg.isNoCut
      cfg.isNoCut = true
      val res0 = p.parseRec(cfg, index)
      cfg.isNoCut = oldNoCut
      val res = res0 match{
        case s: Mutable.Success[_, Elem, Repr] => fail(cfg.failure, s.index)
        case f: Mutable.Failure[Elem, Repr] => success(cfg.success, (), index, Set.empty, false)
      }
      res
    }
    override def opPred = Precedence.PrefixOp
    override def toString = s"!($p)"
  }


  /**
   * Wraps a parser and succeeds with `Some` if [[p]] succeeds,
   * and succeeds with `None` if [[p]] fails.
   */
  case class Optional[+T, R, Elem, Repr](p: Parser[T, Elem, Repr])
                                        (implicit ev: Implicits.Optioner[T, R],
                                         repr: ReprOps[Elem, Repr]) extends Parser[R, Elem, Repr]{

    def parseRec(cfg: ParseCtx[Elem, Repr], index: Int) = {
      val oldFork = cfg.isFork
      cfg.isFork = true
      val res = p.parseRec(cfg, index)
      cfg.isFork = oldFork

      res match{
        case s: Mutable.Success[_, Elem, Repr] =>
          success(cfg.success, ev.some(s.value), s.index, s.traceParsers, s.cut)
        case f: Mutable.Failure[Elem, Repr] if f.cut => failMore(f, index, cfg.logDepth)
        case _ => success(cfg.success, ev.none, index, Set.empty, false)
      }
    }
    override def toString = s"${opWrap(p)}.?"
  }


  /**
   * Contains an optimized version of [[Sequence]] called [[Sequence.Flat]] that
   * combines a tree of [[Sequence]] nodes from the left, into a single
   * tail-recursive function working over a `Vector` of their contents.
   *
   * Intentionally completely type-unsafe internally, using casting all
   * over the place, because it's near impossible to make the variable-length
   * heterogenous-typed list type-safe without going crazy. If constructed by
   * `flatten`-ing out a [[Sequence]], the types are checked when the [[Sequence]]
   * was constructed, so it's still safe.
   *
   * Appears to speed up the scalaparse.PerfTests benchmark by around 2.5x
   */
  object Sequence{

    /**
     * The contents of a [[Sequence]] node, minus the left subtree.
     */
    case class Chain[R, Elem, Repr](p: Parser[R, Elem, Repr], cut: Boolean)
                                   (val ev: Implicits.Sequencer[R, R, R])

    case class Flat[R, Elem, Repr](p0: Parser[R, Elem, Repr],
                                   ps: ArrayBuffer[Chain[R, Elem, Repr]])
                                  (implicit repr: ReprOps[Elem, Repr])
      extends Parser[R, Elem, Repr] {

      def parseRec(cfg: ParseCtx[Elem, Repr], index: Int): Mutable[R, Elem, Repr] = {
        /**
         * Given
         *
         * A ~ B ~ C ~ D
         *
         * Perform the following iterations:
         *
         * rB = evB(pA, pB)
         * rC = evC(rB, pC)
         * rD = evD(rC, pD)
         * return rD
         */
        @tailrec def rec(r1: R,
                         rIndex: Int,
                         rCut: Boolean,
                         vIndex: Int,
                         traceParsers: Set[Parser[_, Elem, Repr]]): Mutable[R, Elem, Repr] = {
          val currParserCut = if (vIndex < ps.length) ps(vIndex).cut else false
          if (rIndex > index && cfg.checkForDrop(rCut | currParserCut)) cfg.input.dropBuffer(rIndex)

          if (vIndex >= ps.length) success(cfg.success, r1, rIndex, traceParsers, rCut)
          else {
            val c = ps(vIndex)
            c.p.parseRec(cfg, rIndex) match {
              case f: Mutable.Failure[Elem, Repr] => failMore(
                f,
                rIndex,
                cfg.logDepth,
                traceParsers = mergeTrace(cfg.traceIndex, f.traceParsers, traceParsers),
                cut = c.cut | f.cut | rCut
              )
              case Mutable.Success(value0, index0, traceParsers0, cut0)  =>
                rec(
                  c.ev(r1, value0),
                  index0,
                  c.cut | cut0 | rCut,
                  vIndex + 1,
                  traceParsers0 | traceParsers
                )
            }
          }
        }
        p0.parseRec(cfg, index) match{
          case f: Mutable.Failure[Elem, Repr] => failMore(f, index, cfg.logDepth, cut = f.cut)
          case Mutable.Success(value0, index0, traceParsers0, cut0) =>
            rec(value0, index0, cut0, 0, traceParsers0)
        }
      }
      override def opPred = Precedence.OtherOp
      override def toString = {

        val rhs = for(c <- ps) yield {
          " ~" + (if (c.cut) "/" else "") + " " + opWrap(c.p)
        }
        s"${opWrap(p0)}${rhs.mkString}"
      }
    }

    /**
     * The types here are all lies. It's ok, just trust the
     * code to do the right thing!
     *
     *
     * A ~ B ~ C ~ D
     * ((A ~ B) ~ C) ~ D
     */
    def flatten[R, Elem, Repr](s: Sequence[R, R, R, Elem, Repr])
                              (implicit repr: ReprOps[Elem, Repr]): Flat[R, Elem, Repr] = {
      def rec(s: Sequence[R, R, R, Elem, Repr]): Flat[R, Elem, Repr] = {
        val ev2 = s.ev2.asInstanceOf[Implicits.Sequencer[R, R, R]]
        s.p1 match{
          case f: Flat[R, Elem, Repr] =>
            f.copy(ps = f.ps :+ Chain[R, Elem, Repr](s.p2, s.cut)(ev2))
          case p: Sequence[R, R, R, Elem, Repr] =>
            val res = rec(p)
            res.copy(ps = res.ps :+ Chain[R, Elem, Repr](s.p2, s.cut)(ev2))
          case p => Flat(p, ArrayBuffer(Chain[R, Elem, Repr](s.p2, s.cut)(ev2)))
        }
      }
      rec(s)
    }
  }

  /**
   * Parsers two things in a row, returning a tuple of the two
   * results if both things succeed
   */
  case class Sequence[+T1, +T2, R,
                      Elem, Repr](p1: Parser[T1, Elem, Repr],
                                  p2: Parser[T2, Elem, Repr], cut: Boolean)
                                 (implicit ev: Implicits.Sequencer[T1, T2, R],
                                  repr: ReprOps[Elem, Repr]) extends Parser[R, Elem, Repr]{
    def ev2: Implicits.Sequencer[_, _, _] = ev
    def parseRec(cfg: ParseCtx[Elem, Repr], index: Int) = {

      p1.parseRec(cfg, index) match{
        case f: Mutable.Failure[Elem, Repr] =>
          failMore(
            f, index, cfg.logDepth,
            traceParsers = mergeTrace(cfg.traceIndex, Set(p1), Set.empty),
            cut = f.cut
          )
        case Mutable.Success(value0, index0, traceParsers0, cut0)  =>
          if (index0 > index && cfg.checkForDrop(cut | cut0)) cfg.input.dropBuffer(index0)

          p2.parseRec(cfg, index0) match{
            case f: Mutable.Failure[Elem, Repr] => failMore(
              f,
              index,
              cfg.logDepth,
              traceParsers = mergeTrace(cfg.traceIndex, traceParsers0, f.traceParsers),
              cut = cut | f.cut | cut0
            )
            case Mutable.Success(value1, index1, traceParsers1, cut1)  =>
              if (index1 > index0 && cfg.checkForDrop(cut | cut0 | cut)) cfg.input.dropBuffer(index0)

              success(
                cfg.success, ev(value0, value1), index1,
                mergeTrace(cfg.traceIndex, traceParsers1, traceParsers0),
                cut1 | cut0 | cut
              )
          }
      }
    }
    override def opPred = Precedence.OtherOp
    override def toString = {
      val op = if(cut) "~/" else "~"
      opWrap(p1) + " " + op + " " + opWrap(p2)
    }
  }


  case class Cut[T, Elem, Repr](p: Parser[T, Elem, Repr])
                               (implicit repr: ReprOps[Elem, Repr])extends Parser[T, Elem, Repr]{
    def parseRec(cfg: ParseCtx[Elem, Repr], index: Int) = {
      p.parseRec(cfg, index) match{
        case f: Mutable.Failure[Elem, Repr] => failMore(f, index, cfg.logDepth, cut = false)
        case s: Mutable.Success[T, Elem, Repr] =>
          if (s.index > index && !cfg.isCapturing && !cfg.isNoCut)
            cfg.input.dropBuffer(s.index)
          success(s, s.value, s.index, s.traceParsers, cut = true)
      }
    }
    override def opPred = Precedence.OtherOp
    override def toString = p.toString
  }
  /**
   * Repeats the parser over and over. Succeeds with a `collection.Seq` of results
   * if there are more than [[min]] and less than [[max]] successful parses.
   * The range [[min]] and [[max]] bounds are inclusive.
   * It uses the [[delimiter]] parser between parses and discards its results.
   */
  case class Repeat[T, +R, Elem, Repr](p: Parser[T, Elem, Repr], min: Int, max: Int,
                                       delimiter: Parser[_, Elem, Repr])
                                      (implicit ev: Implicits.Repeater[T, R],
                                       repr: ReprOps[Elem, Repr]) extends Parser[R, Elem, Repr]{


    def parseRec(cfg: ParseCtx[Elem, Repr], index: Int) = {
      @tailrec def rec(index: Int,
                       del: Parser[_, Elem, Repr],
                       lastFailure: Mutable.Failure[Elem, Repr],
                       acc: ev.Acc,
                       cut: Boolean,
                       count: Int): Mutable[R, Elem, Repr] = {
        val oldFork = cfg.isFork
        cfg.isFork = true
        val resDel = del.parseRec(cfg, index)
        cfg.isFork = oldFork

        resDel match{
          case f1: Mutable.Failure[Elem, Repr] =>
            val cut1 = f1.cut
            if (cut1) failMore(f1, index, cfg.logDepth, cut = true)
            else passInRange(cut, f1, index, ev.result(acc), count)

          case Mutable.Success(value0, index0, traceParsers0, cut0)  =>
            cfg.isFork = true
            val res = p.parseRec(cfg, index0)
            cfg.isFork = oldFork

            res match{
              case f2: Mutable.Failure[Elem, Repr] =>
                val cut2 = f2.cut
                if (cut2 | cut0) failMore(f2, index0, cfg.logDepth, cut = true)
                else passInRange(cut | cut0, f2, index, ev.result(acc), count)

              case Mutable.Success(value1, index1, traceParsers1, cut1)  =>
                ev.accumulate(value1, acc)
                val counted = count + 1
                if (counted < max)
                  rec(index1, delimiter, lastFailure, acc, cut0 | cut1, counted)
                else
                  passInRange(cut0 | cut1, lastFailure, index1, ev.result(acc), counted)
            }
        }
      }

      def passInRange(cut: Boolean,
                      lastFailure: Mutable.Failure[Elem, Repr],
                      finalIndex: Int,
                      acc: R,
                      count: Int) = {
        if (min <= count) {
          val parsers =
            if (null == lastFailure) Set.empty[Parser[_, Elem, Repr]]
            else lastFailure.traceParsers
          success(cfg.success, acc, finalIndex, parsers, cut)
        } else failMore(lastFailure, index, cfg.logDepth, cut = cut)
      }

      // don't call the parseRec at all, if max is "0", as our parser corresponds to `Pass` in that case.
      if (max == 0 ) {
        success(cfg.success, ev.result(ev.initial), index, Set.empty[Parser[_, Elem, Repr]], false)
      } else {
        rec(index, Pass[Elem, Repr], null, ev.initial, false, 0)
      }
    }
    override def toString = {
      val things = collection.Seq(
        if (min == 0) None else Some(min),
        if (delimiter == Pass[Elem, Repr]) None else Some("sep = " + delimiter),
        if (max == Int.MaxValue) None else Some("max = " + max)
      ).flatten.mkString(", ")
      if (things.isEmpty) opWrap(p) + ".rep"
      else s"${opWrap(p)}.rep($things)"
    }
  }

  object Either{
    def flatten[T, Elem, Repr](p: Vector[Parser[T, Elem, Repr]]): Vector[Parser[T, Elem, Repr]] = p.flatMap{
      case Either(ps@_*) => ps
      case p => Vector(p)
    }
  }
  /**
   * Parses using one parser or the other, if the first one fails. Returns
   * the first one that succeeds and fails if both fail
   */
  case class Either[T, Elem, Repr](ps: Parser[T, Elem, Repr]*)
                                  (implicit repr: ReprOps[Elem, Repr]) extends Parser[T, Elem, Repr]{
    private[this] val ps0 = ps.toArray
    private[this] val n = ps0.length
    def parseRec(cfg: ParseCtx[Elem, Repr], index: Int) = {
      @tailrec def rec(parserIndex: Int, traceParsers: Set[Parser[_, Elem, Repr]]): Mutable[T, Elem, Repr] = {
        if (parserIndex >= n) fail(cfg.failure, index)
        else {
          var res: Mutable[T, Elem, Repr] = null

          if (parserIndex == n - 1) {
            res = ps0(parserIndex).parseRec(cfg, index)
          } else {
            val oldFork = cfg.isFork
            cfg.isFork = true
            res = ps0(parserIndex).parseRec(cfg, index)
            cfg.isFork = oldFork
          }

          res match {
            case s: Mutable.Success[T, Elem, Repr] =>
              s.traceParsers = mergeTrace(cfg.traceIndex, s.traceParsers, traceParsers)
              s
            case f: Mutable.Failure[Elem, Repr] if f.cut => failMore(f, index, cfg.logDepth)
            case f: Mutable.Failure[Elem, Repr] => rec(
              parserIndex + 1,
              mergeTrace(cfg.traceIndex, f.traceParsers, traceParsers)
            )
          }
        }
      }
      rec(0, Set.empty)
    }
    override def opPred = if (ps.length == 1) ps(0).opPred else Precedence.|
    override def toString = {
      ps.map(opWrap).mkString(" | ")
    }
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy