Skip to content

Commit

Permalink
Fixed perf issue; added support for trailing whitespace
Browse files Browse the repository at this point in the history
Affects #15
  • Loading branch information
djspiewak committed Jan 16, 2017
1 parent 75a89b5 commit 004f18d
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 10 deletions.
43 changes: 35 additions & 8 deletions core/src/main/scala/parseback/parsers.scala
Expand Up @@ -52,22 +52,49 @@ sealed trait Parser[+A] {
* A prerequisite step would be to convert a Stream[Task, Bytes] (or whatever format and
* effect it is in) into a Stream[Task, Line], which could then in turn be converted
* pretty trivially into a Pull[Task, LineStream, Nothing].
*
* Please note that F[_] should be lazy and stack-safe. If F[_] is not stack-safe,
* this function will SOE on inputs with a very large number of lines.
*/
final def apply[F[+_]: Monad](ls: LineStream[F]): F[List[ParseError] \/ List[A]] = {
ls.normalize flatMap {
case LineStream.More(line, tail) =>
final def apply[F[+_]: Monad](ls: LineStream[F])(implicit W: Whitespace): F[List[ParseError] \/ List[A]] = {
import LineStream._

def stripTrailing(r: SRegex)(ls: LineStream[F]): F[LineStream[F]] = ls match {
case More(line, tail) =>
tail map {
case ls @ More(_, _) =>
More(line, stripTrailing(r)(ls))

case Empty() =>
val base2 = r.replaceAllIn(line.base, "")
More(Line(base2, line.lineNo, line.colNo), Monad[F] pure Empty[F]())
}

case Empty() => Monad[F] pure Empty[F]()
}

def inner(self: Parser[A])(ls: LineStream[F]): F[List[ParseError] \/ List[A]] = ls match {
case More(line, tail) =>
trace(s"current line = ${line.project}")
val derivation = derive(line)
val derivation = self derive line

val ls2: F[LineStream[F]] = line.next map { l =>
Monad[F].pure(LineStream.More(l, tail))
Monad[F] pure More(l, tail)
} getOrElse tail

ls2 flatMap { derivation(_) }
ls2 flatMap inner(derivation)

case LineStream.Empty() =>
Monad[F].pure(finish(Set()))
case Empty() =>
Monad[F] pure (self finish Set())
}

val recompiled =
Some(s"""${W.regex.toString}$$"""r) filter { _ => W.regex.toString != "" }

val stripper =
recompiled map stripTrailing getOrElse { ls: LineStream[F] => Monad[F] pure ls }

stripper(ls) flatMap { _.normalize } flatMap inner(this)
}

protected final def isNullable: Boolean = {
Expand Down
4 changes: 2 additions & 2 deletions core/src/test/scala/parseback/ParsebackSpec.scala
Expand Up @@ -22,7 +22,7 @@ import org.specs2.specification.SpecificationFeatures

trait ParsebackSpec extends Spec with SpecificationFeatures {

final def parseOk[A](input: String)(results: A*): Matcher[Parser[A]] = { p: Parser[A] =>
final def parseOk[A](input: String)(results: A*)(implicit W: Whitespace): Matcher[Parser[A]] = { p: Parser[A] =>
val maybeResults = p(LineStream(input)).value

maybeResults match {
Expand All @@ -42,7 +42,7 @@ trait ParsebackSpec extends Spec with SpecificationFeatures {
}
}

final def failToParse(input: String)(errors: ParseError*): Matcher[Parser[_]] = { p: Parser[_] =>
final def failToParse(input: String)(errors: ParseError*)(implicit W: Whitespace): Matcher[Parser[_]] = { p: Parser[_] =>
val maybeResults = p(LineStream(input)).value

maybeResults match {
Expand Down
12 changes: 12 additions & 0 deletions core/src/test/scala/parseback/RegexSpec.scala
Expand Up @@ -44,5 +44,17 @@ object RegexSpec extends ParsebackSpec {

expr should parseOk("1 + 2")(3)
}

"handle a simple arithmetic grammar with trailing whitespace" in {
implicit val W = Whitespace("""\s+""".r)

lazy val expr: Parser[Int] = (
expr ~ "+" ~ expr ^^ { (_, a, _, b) => a + b }
| expr ~ "-" ~ expr ^^ { (_, a, _, b) => a - b }
| """\d+""".r ^^ { (_, str) => str.toInt }
)

expr should parseOk("1 + 2 ")(3)
}
}
}

0 comments on commit 004f18d

Please sign in to comment.