Skip to content
This repository was archived by the owner on Feb 23, 2018. It is now read-only.

Commit 009b827

Browse files
author
moors
committed
closes #3446 and improves parsing speed by encoding lazy arguments using CBN args and lazy vals.
should avoid needlessly recomputing parsers review by plocinic (so you can include a version that uses the direct support for lazy args in your branch) git-svn-id: http://lampsvn.epfl.ch/svn-repos/scala/scala/trunk@24422 5e8d7ff9-d8ef-0310-90f0-a4852d11357a
1 parent e834635 commit 009b827

File tree

1 file changed

+39
-13
lines changed

1 file changed

+39
-13
lines changed

src/library/scala/util/parsing/combinator/Parsers.scala

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ package scala.util.parsing.combinator
1212
import scala.util.parsing.input._
1313
import scala.collection.mutable.ListBuffer
1414
import scala.annotation.tailrec
15+
import annotation.migration
1516

1617
// TODO: better error handling (labelling like parsec's <?>)
1718

@@ -204,8 +205,10 @@ trait Parsers {
204205

205206
// no filter yet, dealing with zero is tricky!
206207

207-
def append[U >: T](p: => Parser[U]): Parser[U]
208-
= Parser{ in => this(in) append p(in)}
208+
@migration(2, 9, "As of 2.9, the call-by-name argument is evaluated at most once per constructed Parser object, instead of on every need that arises during parsing.")
209+
def append[U >: T](p0: => Parser[U]): Parser[U] = { lazy val p = p0 // lazy argument
210+
Parser{ in => this(in) append p(in)}
211+
}
209212

210213

211214
// the operator formerly known as +++, ++, &, but now, behold the venerable ~
@@ -217,22 +220,28 @@ trait Parsers {
217220
* <p> `p ~ q' succeeds if `p' succeeds and `q' succeeds on the input
218221
* left over by `p'.</p>
219222
*
220-
* @param q a parser that will be executed after `p' (this parser) succeeds
223+
* @param q a parser that will be executed after `p' (this parser) succeeds -- evaluated at most once, and only when necessary
221224
* @return a `Parser' that -- on success -- returns a `~' (like a Pair, but easier to pattern match on)
222225
* that contains the result of `p' and that of `q'.
223226
* The resulting parser fails if either `p' or `q' fails.
224227
*/
225-
def ~ [U](p: => Parser[U]): Parser[~[T, U]] = (for(a <- this; b <- p) yield new ~(a,b)).named("~")
228+
@migration(2, 9, "As of 2.9, the call-by-name argument is evaluated at most once per constructed Parser object, instead of on every need that arises during parsing.")
229+
def ~ [U](q: => Parser[U]): Parser[~[T, U]] = { lazy val p = q // lazy argument
230+
(for(a <- this; b <- p) yield new ~(a,b)).named("~")
231+
}
226232

227233
/** A parser combinator for sequential composition which keeps only the right result
228234
*
229235
* <p> `p ~> q' succeeds if `p' succeeds and `q' succeeds on the input
230236
* left over by `p'.</p>
231237
*
232-
* @param q a parser that will be executed after `p' (this parser) succeeds
238+
* @param q a parser that will be executed after `p' (this parser) succeeds -- evaluated at most once, and only when necessary
233239
* @return a `Parser' that -- on success -- returns the result of `q'.
234240
*/
235-
def ~> [U](p: => Parser[U]): Parser[U] = (for(a <- this; b <- p) yield b).named("~>")
241+
@migration(2, 9, "As of 2.9, the call-by-name argument is evaluated at most once per constructed Parser object, instead of on every need that arises during parsing.")
242+
def ~> [U](q: => Parser[U]): Parser[U] = { lazy val p = q // lazy argument
243+
(for(a <- this; b <- p) yield b).named("~>")
244+
}
236245

237246
/** A parser combinator for sequential composition which keeps only the left result
238247
*
@@ -241,10 +250,13 @@ trait Parsers {
241250
*
242251
* <b>Note:</b> &lt;~ has lower operator precedence than ~ or ~>.
243252
*
244-
* @param q a parser that will be executed after `p' (this parser) succeeds
253+
* @param q a parser that will be executed after `p' (this parser) succeeds -- evaluated at most once, and only when necessary
245254
* @return a `Parser' that -- on success -- returns the result of `p'.
246255
*/
247-
def <~ [U](p: => Parser[U]): Parser[T] = (for(a <- this; b <- p) yield a).named("<~")
256+
@migration(2, 9, "As of 2.9, the call-by-name argument is evaluated at most once per constructed Parser object, instead of on every need that arises during parsing.")
257+
def <~ [U](q: => Parser[U]): Parser[T] = { lazy val p = q // lazy argument
258+
(for(a <- this; b <- p) yield a).named("<~")
259+
}
248260

249261
/* not really useful: V cannot be inferred because Parser is covariant in first type parameter (V is always trivially Nothing)
250262
def ~~ [U, V](q: => Parser[U])(implicit combine: (T, U) => V): Parser[V] = new Parser[V] {
@@ -286,10 +298,12 @@ trait Parsers {
286298
* If `p' and `q' both succeed, the parser that consumed the most
287299
* characters accepts.</p>
288300
*
289-
* @param q a parser that accepts if p consumes less characters.
301+
* @param q0 a parser that accepts if p consumes less characters. -- evaluated at most once, and only when necessary
290302
* @return a `Parser' that returns the result of the parser consuming the most characters (out of `p' and `q').
291303
*/
292-
def ||| [U >: T](q: => Parser[U]): Parser[U] = new Parser[U] {
304+
@migration(2, 9, "As of 2.9, the call-by-name argument is evaluated at most once per constructed Parser object, instead of on every need that arises during parsing.")
305+
def ||| [U >: T](q0: => Parser[U]): Parser[U] = new Parser[U] {
306+
lazy val q = q0 // lazy argument
293307
def apply(in: Input) = {
294308
val res1 = Parser.this(in)
295309
val res2 = q(in)
@@ -316,7 +330,17 @@ trait Parsers {
316330
def ^^ [U](f: T => U): Parser[U] = map(f).named(toString+"^^")
317331

318332

319-
def ^^^ [U](r: U): Parser[U] = ^^ (x => r)
333+
/** A parser combinator that changes a successful result into the specified value.
334+
*
335+
* <p>`p ^^^ v' succeeds if `p' succeeds; discards its result, and returns `v` instead.</p>
336+
* @param v The new result for the parser, evaluated at most once (if `p` succeeds), not evaluated at all if `p` fails.
337+
* @return a parser that has the same behaviour as the current parser, but whose successful result is `v`
338+
*/
339+
@migration(2, 9, "As of 2.9, the call-by-name argument is evaluated at most once per constructed Parser object, instead of on every need that arises during parsing.")
340+
def ^^^ [U](v: => U): Parser[U] = new Parser[U] {
341+
lazy val v0 = v // lazy argument
342+
def apply(in: Input) = Parser.this(in) map (x => v0)
343+
}.named(toString+"^^^")
320344

321345
/** A parser combinator for partial function application
322346
*
@@ -556,11 +580,13 @@ trait Parsers {
556580
* (the result is a `List' of the consecutive results of `f' and `p')</p>
557581
*
558582
* @param first a `Parser' that parses the first piece of input
559-
* @param p a `Parser' that is to be applied successively to the rest of the input (if any)
583+
* @param p0 a `Parser' that is to be applied successively to the rest of the input (if any) -- evaluated at most once, and only when necessary
560584
* @return A parser that returns a list of results produced by first applying `f' and then
561585
* repeatedly `p' to the input (it only succeeds if `f' matches).
562586
*/
563-
def rep1[T](first: => Parser[T], p: => Parser[T]): Parser[List[T]] = Parser { in =>
587+
@migration(2, 9, "As of 2.9, the p0 call-by-name arguments is evaluated at most once per constructed Parser object, instead of on every need that arises during parsing.")
588+
def rep1[T](first: => Parser[T], p0: => Parser[T]): Parser[List[T]] = Parser { in =>
589+
lazy val p = p0 // lazy argument
564590
val elems = new ListBuffer[T]
565591

566592
def continue(in: Input): ParseResult[List[T]] = {

0 commit comments

Comments
 (0)