Skip to content

Add non backtracking versions of ~> and <~ #38

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 13, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/main/scala/scala/util/parsing/combinator/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,36 @@ trait Parsers {
def ~! [U](p: => Parser[U]): Parser[~[T, U]]
= OnceParser{ (for(a <- this; b <- commit(p)) yield new ~(a,b)).named("~!") }


/** A parser combinator for non-back-tracking sequential composition which only keeps the right result.
*
* `p ~>! q` succeeds if `p` succeds and `q` succeds on the input left over by `p`.
* In case of failure, no back-tracking is performed (in an earlier parser produced by the `|` combinator).
*
* @param q a parser that will be executed after `p` (this parser) succeeds -- evaluated at most once, and only when necessary
* @return a `Parser` that -- on success -- reutrns the result of `q`.
* The resulting parser fails if either `p` or `q` fails, this failure is fatal.
*/
@migration("The call-by-name argument is evaluated at most once per constructed Parser object, instead of on every need that arises during parsing.", "2.9.0")
def ~>! [U](q: => Parser[U]): Parser[U] = { lazy val p = q // lazy argument
OnceParser { (for(a <- this; b <- commit(p)) yield b).named("~>!") }
}

/** A parser combinator for non-back-tracking sequential composition which only keeps the left result.
*
* `p <~! q` succeeds if `p` succeds and `q` succeds on the input left over by `p`.
* In case of failure, no back-tracking is performed (in an earlier parser produced by the `|` combinator).
*
* @param q a parser that will be executed after `p` (this parser) succeeds -- evaluated at most once, and only when necessary
* @return a `Parser` that -- on success -- reutrns the result of `p`.
* The resulting parser fails if either `p` or `q` fails, this failure is fatal.
*/
@migration("The call-by-name argument is evaluated at most once per constructed Parser object, instead of on every need that arises during parsing.", "2.9.0")
def <~! [U](q: => Parser[U]): Parser[T] = { lazy val p = q // lazy argument
OnceParser { (for(a <- this; b <- commit(p)) yield a).named("<~!") }
}


/** A parser combinator for alternative composition.
*
* `p | q` succeeds if `p` succeeds or `q` succeeds.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,33 @@ class RegexParsersTest {
assertEquals(result(5), extractResult(parseAll(q, "5")))
}

@Test
def parserSkippingResult: Unit = {
object parser extends RegexParsers {
def quote = "\""
def string = """[a-zA-Z]*""".r
type ResultType = String
def p: Parser[ResultType] = quote ~> string <~ quote
def q: Parser[ResultType] = quote ~>! string <~! quote
def halfQuoted = quote ~ string ^^ { case q ~ s => q + s }
}
import parser._
val failureLq = parseAll(p, "\"asdf").asInstanceOf[Failure]
val failureRq = parseAll(p, "asdf\"").asInstanceOf[Failure]
val failureQBacktrackL = parseAll(q | quote, "\"").asInstanceOf[Error]
val failureQBacktrackR = parseAll(q | halfQuoted, "\"asdf").asInstanceOf[Error]

val successP = parseAll(p, "\"asdf\"").get
assertEquals(successP, "asdf")
val successPBacktrackL = parseAll(p | quote, "\"").get
assertEquals(successPBacktrackL, "\"")
val successPBacktrackR = parseAll(p | halfQuoted, "\"asdf").get
assertEquals(successPBacktrackR, "\"asdf")

val successQ = parseAll(q, "\"asdf\"").get
assertEquals(successQ, "asdf")
}

@Test
def parserFilter: Unit = {
object parser extends RegexParsers {
Expand Down