Skip to content

Commit

Permalink
Seq sequence support (#189)
Browse files Browse the repository at this point in the history
  • Loading branch information
aborg0 authored and lihaoyi committed Jul 22, 2018
1 parent 3bc8788 commit f68b463
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 0 deletions.
8 changes: 8 additions & 0 deletions fastparse/shared/src/main/scala/fastparse/Api.scala
@@ -1,8 +1,12 @@
package fastparse
import acyclic.file
import fastparse.core.Implicits.Repeater
import fastparse.core.Parser
import fastparse.parsers.Combinators.SeqSequence

import language.experimental.macros
import fastparse.parsers.Intrinsics
import fastparse.parsers.Terminals.Pass
import fastparse.utils.{ElemSetHelper, ReprOps}

import scala.reflect.ClassTag
Expand Down Expand Up @@ -91,6 +95,10 @@ abstract class Api[Elem, Repr](ct: ClassTag[Elem],
def P[T](p: => Parser[T])(implicit name: sourcecode.Name): Parser[T] =
parsers.Combinators.Rule(name.value, () => p)

def sequence[R, V](parsers: Seq[Parser[V]], sep: Parser[_] = Pass)(implicit ev: Repeater[V, R]): Parser[R] = {
SeqSequence[V, R, Elem, Repr](ps = parsers, delimiter = sep)
}

type P0 = Parser[Unit]
type Parser[+T] = core.Parser[T, Elem, Repr]
type P[+T] = Parser[T]
Expand Down
Expand Up @@ -500,6 +500,84 @@ object Combinators {
}
}

/**
* Sequence the parsers. Succeeds with a `Seq` of results
* It uses the [[delimiter]] parser between parsers and discards its results.
*/
case class SeqSequence[T, +R, Elem, Repr](ps: Seq[Parser[T, Elem, Repr]],
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): Mutable[R, Elem, Repr] = {
@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 = ps(count).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 < ps.size)
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 (ps.size == 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 (ps.isEmpty) {
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 =
if (delimiter == Pass[Elem, Repr]) "" else s"sep = $delimiter"

ps.map(p => opWrap(p)).mkString(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
Expand Down
13 changes: 13 additions & 0 deletions fastparse/shared/src/test/scala/fastparse/ExampleTests.scala
Expand Up @@ -57,6 +57,19 @@ object ExampleTests extends TestSuite{
val Parsed.Failure(_, 7, _) = ab4c.parse("ababababac")
}

'seqenceSeq - {
val a = P("a")
val b = P("b")
val ababa = Seq(a, b, a, b, a)
val Parsed.Success(_, 5) = sequence(ababa).parse("ababa")
val Parsed.Failure(_, 2, _) = sequence(ababa).parse("abba")
val Parsed.Success(_, 13) = sequence(ababa, sep = P(", ")).parse("a, b, a, b, a")
val Parsed.Failure(_, 1, _) = sequence(ababa, sep = P(", ")).parse("ababa")
val Parsed.Success(_, 5) = (sequence(ababa) | P("abba")).parse("ababa")
val Parsed.Success(_, 4) = (sequence(ababa) | P("abba")).parse("abba")
val Parsed.Failure(_, 2, _) = (sequence(ababa, sep = Pass ~/ Pass) | P("abba")).parse("abba")
}

'option - {
val option = P( "c".? ~ "a".rep(sep="b").! ~ End)

Expand Down

0 comments on commit f68b463

Please sign in to comment.