From 41c6dc0087679bd4e4376671e60bd9c893ff14e0 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Tue, 8 Mar 2011 23:03:33 +0000 Subject: closes #3446 and improves parsing speed by enco... 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) --- .../scala/util/parsing/combinator/Parsers.scala | 52 ++++++++++++++++------ 1 file changed, 39 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/library/scala/util/parsing/combinator/Parsers.scala b/src/library/scala/util/parsing/combinator/Parsers.scala index 4dd76ce0a0..a02f33ef36 100644 --- a/src/library/scala/util/parsing/combinator/Parsers.scala +++ b/src/library/scala/util/parsing/combinator/Parsers.scala @@ -12,6 +12,7 @@ package scala.util.parsing.combinator import scala.util.parsing.input._ import scala.collection.mutable.ListBuffer import scala.annotation.tailrec +import annotation.migration // TODO: better error handling (labelling like parsec's ) @@ -204,8 +205,10 @@ trait Parsers { // no filter yet, dealing with zero is tricky! - def append[U >: T](p: => Parser[U]): Parser[U] - = Parser{ in => this(in) append p(in)} + @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.") + def append[U >: T](p0: => Parser[U]): Parser[U] = { lazy val p = p0 // lazy argument + Parser{ in => this(in) append p(in)} + } // the operator formerly known as +++, ++, &, but now, behold the venerable ~ @@ -217,22 +220,28 @@ trait Parsers { *

`p ~ q' succeeds if `p' succeeds and `q' succeeds on the input * left over by `p'.

* - * @param q a parser that will be executed after `p' (this parser) succeeds + * @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 -- returns a `~' (like a Pair, but easier to pattern match on) * that contains the result of `p' and that of `q'. * The resulting parser fails if either `p' or `q' fails. */ - def ~ [U](p: => Parser[U]): Parser[~[T, U]] = (for(a <- this; b <- p) yield new ~(a,b)).named("~") + @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.") + def ~ [U](q: => Parser[U]): Parser[~[T, U]] = { lazy val p = q // lazy argument + (for(a <- this; b <- p) yield new ~(a,b)).named("~") + } /** A parser combinator for sequential composition which keeps only the right result * *

`p ~> q' succeeds if `p' succeeds and `q' succeeds on the input * left over by `p'.

* - * @param q a parser that will be executed after `p' (this parser) succeeds + * @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 -- returns the result of `q'. */ - def ~> [U](p: => Parser[U]): Parser[U] = (for(a <- this; b <- p) yield b).named("~>") + @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.") + def ~> [U](q: => Parser[U]): Parser[U] = { lazy val p = q // lazy argument + (for(a <- this; b <- p) yield b).named("~>") + } /** A parser combinator for sequential composition which keeps only the left result * @@ -241,10 +250,13 @@ trait Parsers { * * Note: <~ has lower operator precedence than ~ or ~>. * - * @param q a parser that will be executed after `p' (this parser) succeeds + * @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 -- returns the result of `p'. */ - def <~ [U](p: => Parser[U]): Parser[T] = (for(a <- this; b <- p) yield a).named("<~") + @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.") + def <~ [U](q: => Parser[U]): Parser[T] = { lazy val p = q // lazy argument + (for(a <- this; b <- p) yield a).named("<~") + } /* not really useful: V cannot be inferred because Parser is covariant in first type parameter (V is always trivially Nothing) def ~~ [U, V](q: => Parser[U])(implicit combine: (T, U) => V): Parser[V] = new Parser[V] { @@ -286,10 +298,12 @@ trait Parsers { * If `p' and `q' both succeed, the parser that consumed the most * characters accepts.

* - * @param q a parser that accepts if p consumes less characters. + * @param q0 a parser that accepts if p consumes less characters. -- evaluated at most once, and only when necessary * @return a `Parser' that returns the result of the parser consuming the most characters (out of `p' and `q'). */ - def ||| [U >: T](q: => Parser[U]): Parser[U] = new Parser[U] { + @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.") + def ||| [U >: T](q0: => Parser[U]): Parser[U] = new Parser[U] { + lazy val q = q0 // lazy argument def apply(in: Input) = { val res1 = Parser.this(in) val res2 = q(in) @@ -316,7 +330,17 @@ trait Parsers { def ^^ [U](f: T => U): Parser[U] = map(f).named(toString+"^^") - def ^^^ [U](r: U): Parser[U] = ^^ (x => r) + /** A parser combinator that changes a successful result into the specified value. + * + *

`p ^^^ v' succeeds if `p' succeeds; discards its result, and returns `v` instead.

+ * @param v The new result for the parser, evaluated at most once (if `p` succeeds), not evaluated at all if `p` fails. + * @return a parser that has the same behaviour as the current parser, but whose successful result is `v` + */ + @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.") + def ^^^ [U](v: => U): Parser[U] = new Parser[U] { + lazy val v0 = v // lazy argument + def apply(in: Input) = Parser.this(in) map (x => v0) + }.named(toString+"^^^") /** A parser combinator for partial function application * @@ -556,11 +580,13 @@ trait Parsers { * (the result is a `List' of the consecutive results of `f' and `p')

* * @param first a `Parser' that parses the first piece of input - * @param p a `Parser' that is to be applied successively to the rest of the input (if any) + * @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 * @return A parser that returns a list of results produced by first applying `f' and then * repeatedly `p' to the input (it only succeeds if `f' matches). */ - def rep1[T](first: => Parser[T], p: => Parser[T]): Parser[List[T]] = Parser { in => + @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.") + def rep1[T](first: => Parser[T], p0: => Parser[T]): Parser[List[T]] = Parser { in => + lazy val p = p0 // lazy argument val elems = new ListBuffer[T] def continue(in: Input): ParseResult[List[T]] = { -- cgit v1.2.3