diff options
author | Martin Odersky <odersky@gmail.com> | 2008-04-07 15:57:07 +0000 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2008-04-07 15:57:07 +0000 |
commit | 34fe33a61291ec51cb0598b3702b2c6de8ebb3f2 (patch) | |
tree | e5fe4f022df23cf216cc472d37c22e196328981b | |
parent | 67af71b370af294fef48d78efb3337274dbcecd3 (diff) | |
download | scala-34fe33a61291ec51cb0598b3702b2c6de8ebb3f2.tar.gz scala-34fe33a61291ec51cb0598b3702b2c6de8ebb3f2.tar.bz2 scala-34fe33a61291ec51cb0598b3702b2c6de8ebb3f2.zip |
(1) Removed generation of $tag method for inter...
(1) Removed generation of $tag method for interfaces (2) improved type
inference for clsoures (3) redesign of CharSequence and regex.
19 files changed, 492 insertions, 70 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index fd1db30e74..e7c33cc426 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -2200,9 +2200,7 @@ trait Parsers extends NewScanners with MarkupParsers { template, template.parents, makeSelfDef(nme.WILDCARD, thistpe), template.body) } else syntaxError("`requires' cannot be combined with explicit self type", false) } - val mods1 = if (mods.hasFlag(Flags.TRAIT) && - (template.body forall treeInfo.isInterfaceMember)) - mods | Flags.INTERFACE + val mods1 = if (isInterface(mods, template.body)) mods | Flags.INTERFACE else mods val result = ClassDef(mods1, name, tparams, template) implicitClassViews = savedViews @@ -2271,6 +2269,9 @@ trait Parsers extends NewScanners with MarkupParsers { } } + def isInterface(mods: Modifiers, body: List[Tree]) = + (mods.hasFlag(Flags.TRAIT) && (body forall treeInfo.isInterfaceMember)) + /** ClassTemplateOpt ::= extends ClassTemplate | [[extends] TemplateBody] * TraitTemplateOpt ::= extends TraitTemplate | [[extends] TemplateBody] */ @@ -2286,7 +2287,10 @@ trait Parsers extends NewScanners with MarkupParsers { (List(), List(List()), self, body) } var parents = parents0 - if (name != nme.ScalaObject.toTypeName) parents = parents ::: List(scalaScalaObjectConstr) + if (name != nme.ScalaObject.toTypeName && !isInterface(mods, body)) + parents = parents ::: List(scalaScalaObjectConstr) + if (parents.isEmpty) + parents = List(scalaAnyRefConstr) if (mods.hasFlag(Flags.CASE)) parents = parents ::: List(productConstr) val tree = Template(parents, self, constrMods, vparamss, argss, body) // @S: if nothing parsed, don't use next position! diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 758a006e0e..d0e90a2fe2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -110,7 +110,8 @@ trait Contexts { self: Analyzer => var checking = false var retyping = false - var savedTypeBounds: List[(Symbol, Type)] = List() + var savedTypeBounds: List[(Symbol, Type)] = List() // saved type bounds + // for type parameters which are narrowed in a GADT def intern0 : Context = { if (this eq NoContext) return this diff --git a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala index 7b705036ac..1c6412419d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala +++ b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala @@ -19,6 +19,19 @@ trait EtaExpansion { self: Analyzer => import global._ import posAssigner.atPos + object etaExpansion { + def unapply(tree: Tree): Option[(List[ValDef], Tree, List[Tree])] = tree match { + case Function(vparams, Apply(fn, args)) + if (List.forall2(vparams, args) { + case (vparam, Ident(name)) => vparam.name == name + case _ => false + }) => + Some((vparams, fn, args)) + case _ => + None + } + } + /** <p> * Expand partial function applications of type <code>type</code>. * </p><pre> diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 1223328c96..c118df8a54 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -1150,7 +1150,7 @@ trait Infer { val ptvars = ptparams map freshVar val pt1 = pt.instantiateTypeParams(ptparams, ptvars) if (!isPopulated(tp, pt1)) { - error(pos, "pattern type is incompatibe with expected type"+foundReqMsg(pattp, pt)) + error(pos, "pattern type is incompatible with expected type"+foundReqMsg(pattp, pt)) return pattp } ptvars foreach instantiateTypeVar @@ -1169,7 +1169,7 @@ trait Infer { if (pat.tpe <:< pt1) ptvars foreach instantiateTypeVar else - error(pat.pos, "pattern type is incompatibe with expected type"+foundReqMsg(pat.tpe, pt)) + error(pat.pos, "pattern type is incompatible with expected type"+foundReqMsg(pat.tpe, pt)) } object toOrigin extends TypeMap { diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 12e0e63960..2cdbb3d57d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1449,10 +1449,22 @@ trait Typers { self: Analyzer => errorTree(fun, "wrong number of parameters; expected = " + argpts.length) else { val vparamSyms = List.map2(fun.vparams, argpts) { (vparam, argpt) => - if (vparam.tpt.isEmpty) + if (vparam.tpt.isEmpty) { vparam.tpt.tpe = if (isFullyDefined(argpt)) argpt else { + fun match { + case etaExpansion(vparams, fn, args) if !codeExpected => + println("typing eta "+fn) + silent(_.typed(fn, funMode(mode), pt)) match { + case fn1: Tree => + val ftpe = normalize(fn1.tpe) baseType FunctionClass(fun.vparams.length) + if (isFunctionType(ftpe) && isFullyDefined(ftpe)) + return typedFunction(fun, mode, ftpe) + case _ => + } + case _ => + } error( vparam.pos, "missing parameter type"+ @@ -1460,6 +1472,7 @@ trait Typers { self: Analyzer => else "")) ErrorType } + } enterSym(context, vparam) if (context.retyping) context.scope enter vparam.symbol vparam.symbol @@ -1593,6 +1606,31 @@ trait Typers { self: Analyzer => } } + /** Does function need to be instantiated, because a missing parameter + * in an argument closure overlaps with an uninstantiated formal? + */ + def needsInstantiation(tparams: List[Symbol], formals: List[Type], args: List[Tree]) = { + def isLowerBounded(tparam: Symbol) = { + val losym = tparam.info.bounds.lo.typeSymbol + losym != AllClass && losym != AllRefClass + } + List.exists2(formals, args) { + case (formal, Function(vparams, _)) => + (vparams exists (_.tpt.isEmpty)) && + vparams.length <= MaxFunctionArity && + (formal baseType FunctionClass(vparams.length) match { + case TypeRef(_, _, formalargs) => + List.exists2(formalargs, vparams) ((formalarg, vparam) => + vparam.tpt.isEmpty && (tparams exists (formalarg contains))) && + (tparams forall isLowerBounded) + case _ => + false + }) + case _ => + false + } + } + /** * @param tree ... * @param fun0 ... @@ -1604,12 +1642,19 @@ trait Typers { self: Analyzer => def doTypedApply(tree: Tree, fun0: Tree, args: List[Tree], mode: Int, pt: Type): Tree = { var fun = fun0 if (fun.hasSymbol && (fun.symbol hasFlag OVERLOADED)) { - // preadapt symbol to number of arguments given - val argtypes = args map (arg => AllClass.tpe) + // preadapt symbol to number and shape of arguments given + def shapeType(arg: Tree): Type = arg match { + case Function(vparams, body) => + functionType(vparams map (vparam => AnyClass.tpe), shapeType(body)) + case _ => + AllClass.tpe + } + val argtypes = args map shapeType val pre = fun.symbol.tpe.prefix var sym = fun.symbol filter { alt => isApplicableSafe(context.undetparams, followApply(pre.memberType(alt)), argtypes, pt) } + //println("narrowed to "+sym+":"+sym.info+"/"+argtypes) if (sym hasFlag OVERLOADED) { // eliminate functions that would result from tupling transforms val sym1 = sym filter (alt => hasExactlyNumParams(followApply(alt.tpe), argtypes.length)) @@ -1673,6 +1718,10 @@ trait Typers { self: Analyzer => atPos(tree.pos) { gen.mkNil setType restpe } } else constfold(copy.Apply(tree, fun, args2).setType(ifPatternSkipFormals(restpe))) + } else if (needsInstantiation(tparams, formals, args1)) { + //println("needs inst "+fun+" "+tparams+"/"+(tparams map (_.info))) + inferExprInstance(fun, tparams, WildcardType) + doTypedApply(tree, fun, args1, mode, pt) } else { assert((mode & PATTERNmode) == 0); // this case cannot arise for patterns val lenientTargs = protoTypeArgs(tparams, formals, mt.resultApprox, pt) @@ -3527,9 +3576,14 @@ trait Typers { self: Analyzer => List() } - def implicitManifest(pt: Type): Tree = pt match { - case TypeRef(_, ManifestClass, List(arg)) => manifestOfType(pos, arg) - case _ => EmptyTree + def implicitManifest(pt: Type): Tree = { + // test below is designed so that ManifestClass need not be loaded + // (because it's not available everywhere) + if (pt.typeSymbol.fullNameString == "scala.reflect.Manifest") + pt match { + case TypeRef(_, ManifestClass, List(arg)) => manifestOfType(pos, arg) + } + else EmptyTree } var tree = searchImplicit(context.implicitss, true) diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index f2247f813c..e077329d4c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -135,7 +135,7 @@ trait Unapplies { self: Analyzer => */ def caseModuleDef(cdef: ClassDef): ModuleDef = atPos(cdef.pos) { var parents = List(gen.scalaScalaObjectConstr) - if (cdef.tparams.isEmpty && constrParams(cdef).length == 1) + if (!(cdef.mods hasFlag ABSTRACT) && cdef.tparams.isEmpty && constrParams(cdef).length == 1) parents = gen.scalaFunctionConstr(constrParams(cdef).head map (_.tpt), Ident(cdef.name)) :: parents ModuleDef( diff --git a/src/library/scala/Predef.scala b/src/library/scala/Predef.scala index c7a01d6642..387f5c92d3 100644 --- a/src/library/scala/Predef.scala +++ b/src/library/scala/Predef.scala @@ -344,6 +344,12 @@ object Predef { implicit def forceRandomAccessCharSeq(x: runtime.RichString): String = x.mkString implicit def lazyStreamToConsable[A](xs: => Stream[A]) = new runtime.StreamCons(xs) - def currentThread = java.lang.Thread.currentThread() + implicit def seqToCharSequence(xs: RandomAccessSeq[Char]): CharSequence = new CharSequence { + def length: Int = xs.length + def charAt(index: Int): Char = xs(index) + def subSequence(start: Int, end: Int): CharSequence = seqToCharSequence(xs.slice(start, end)) + override def toString: String = xs.mkString("") + } + def currentThread = java.lang.Thread.currentThread() } diff --git a/src/library/scala/Seq.scala b/src/library/scala/Seq.scala index bc075fb745..6b545ae6e7 100644 --- a/src/library/scala/Seq.scala +++ b/src/library/scala/Seq.scala @@ -313,8 +313,8 @@ trait Seq[+A] extends AnyRef with PartialFunction[Int, A] with Collection[A] { result.toList } - /** A sub-sequence of <code>len</code> elements - * starting at index <code>from</code> (non-strict) + /** A sub-sequence starting at index <code>from</code> + * and ending (non-inclusive) at index <code>until</code> (non-strict) * * @param from The index of the first element of the slice * @param until The index of the element following the slice @@ -323,6 +323,14 @@ trait Seq[+A] extends AnyRef with PartialFunction[Int, A] with Collection[A] { */ def slice(from: Int, until: Int): Seq[A] = drop(from).take(until - from) + /** A sub-sequence starting at index <code>from</code> + * and extending up to the length of the current sequence (non-strict) + * + * @param from The index of the first element of the slice + * @throws IndexOutOfBoundsException if <code>from < 0</code> + */ + def slice(from: Int): Seq[A] = slice(from, length) + /** Returns the longest prefix of this sequence whose elements satisfy * the predicate <code>p</code>. * diff --git a/src/library/scala/StringBuilder.scala b/src/library/scala/StringBuilder.scala index 92ee3b9ac8..f6da576b07 100644 --- a/src/library/scala/StringBuilder.scala +++ b/src/library/scala/StringBuilder.scala @@ -943,4 +943,11 @@ object StringBuilder { } -1 } + + implicit def toCharSequence(sb: StringBuilder): java.lang.CharSequence = new java.lang.CharSequence { + def length: Int = sb.length + def charAt(index: Int): Char = sb.charAt(index) + def subSequence(start: Int, end: Int): java.lang.CharSequence = sb.substring(start, end) + override def toString: String = sb.toString + } } diff --git a/src/library/scala/collection/immutable/PagedSeq.scala b/src/library/scala/collection/immutable/PagedSeq.scala new file mode 100755 index 0000000000..de034e67dd --- /dev/null +++ b/src/library/scala/collection/immutable/PagedSeq.scala @@ -0,0 +1,245 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2006-2007, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: PagedSeq.scala 14497 2008-04-04 12:09:06Z washburn $ + + +package scala.collection.immutable + +import java.io._ +import util.matching.Regex + + +/** The PagedSeq object defines a lazy implementations of + * a random access sequence. + */ +object PagedSeq { + final val UndeterminedEnd = Math.MAX_INT + + /** Constructs a character sequence from a character iterator */ + def fromIterator[T](source: Iterator[T]): PagedSeq[T] = + new PagedSeq[T]((data: Array[T], start: Int, len: Int) => { + var i = 0 + while (i < len && source.hasNext) { + data(start + i) = source.next + i += 1 + } + if (i == 0) -1 else i + }) + + /** Constructs a character sequence from a character iterable */ + def fromIterable[T](source: Iterable[T]): PagedSeq[T] = + fromIterator(source.elements) + + /** Constructs a character sequence from a string iterator */ + def fromStrings(source: Iterator[String]): PagedSeq[Char] = { + var current: String = "" + def more(data: Array[Char], start: Int, len: Int): Int = + if (current.length != 0) { + val cnt = current.length min len + current.getChars(0, cnt, data, start) + current = current.substring(cnt) + if (cnt == len) cnt + else (more(data, start + cnt, len - cnt) max 0) + cnt + } else if (source.hasNext) { + current = source.next + more(data, start, len) + } else -1 + new PagedSeq(more(_: Array[Char], _: Int, _: Int)) + } + + /** Constructs a character sequence from a string iterable */ + def fromStrings(source: Iterable[String]): PagedSeq[Char] = + fromStrings(source.elements) + + /** Constructs a character sequence from a line iterator + * Lines do not contain trailing `\n' characters; The method inserts + * a line separator `\n' between any two lines in the sequence. + */ + def fromLines(source: Iterator[String]): PagedSeq[Char] = { + var isFirst = true + fromStrings(source map { line => + if (isFirst) line + else { + isFirst = false + "\n"+line + } + }) + } + + /** Constructs a character sequence from a line iterable + * Lines do not contain trailing `\n' characters; The method inserts + * a line separator `\n' between any two lines in the sequence. + */ + def fromLines(source: Iterable[String]): PagedSeq[Char] = + fromLines(source.elements) + + /** Constructs a character sequence from an input reader + */ + def fromReader(source: Reader): PagedSeq[Char] = + new PagedSeq(source.read(_: Array[Char], _: Int, _: Int)) + + /** Constructs a character sequence from an input file + */ + def fromFile(source: File): PagedSeq[Char] = + fromReader(new FileReader(source)) + + /** Constructs a character sequence from a file with given name + */ + def fromFile(source: String): PagedSeq[Char] = + fromFile(new File(source)) + + /** Constructs a character sequence from a scala.io.Source value + */ + def fromSource(source: io.Source) = + fromLines(source.getLines) +} + + +import PagedSeq._ + +/** An implementation of lazily computed sequences, where elements are stored + * in ``pages'', i.e. arrays of fixed size. + * + * @author Martin Odersky + */ +class PagedSeq[T] protected (more: (Array[T], Int, Int) => Int, + first: Page[T], start: Int, end: Int) extends RandomAccessSeq[T] { + + /** A paged sequence is constructed from a method that produces more characters when asked. + * The producer method is analogous to the read method in java.io.Reader. + * It takes three parameters: an array of characters, a start index, and an end index. + * It should try to fill the array between start and end indices (not including end index). + * It returns the number of characters produced, or -1 if end of logical input stream was reached + * before any character was read. + */ + def this(more: (Array[T], Int, Int) => Int) = this(more, new Page[T](0), 0, UndeterminedEnd) + + private var current: Page[T] = first + + private def latest = first.latest + + private def addMore() = latest.addMore(more) + + private def page(absindex: Int) = { + if (absindex < current.start) + current = first + while (absindex >= current.end && current.next != null) + current = current.next + while (absindex >= current.end && !current.isLast) { + current = addMore() + } + current + } + + /** The length of the character sequence + * Note: calling this method will force sequence to be read until the end. + */ + def length: Int = { + while (!latest.isLast) addMore() + (latest.end min end) - start + } + + /** The character at position `index'. + */ + def apply(index: Int) = + if (isDefinedAt(index)) page(index + start)(index + start) + else throw new IndexOutOfBoundsException(index.toString) + + /** Is character sequence defined at `index'? + * Unlike `length' this operation does not force reading + * a lazy sequence to the end. + */ + override def isDefinedAt(index: Int) = + index >= 0 && index < end - start && { + val p = page(index + start); index + start < p.end + } + + /** the subsequence from index `start' up to and excluding + * the minimum of index `end' and the length of the current sequence. + */ + override def slice(_start: Int, _end: Int) = { + page(start) + val s = start + _start + val e = if (_end == UndeterminedEnd) _end else start + _end + var f = first + while (f.end <= s && !f.isLast) f = f.next + new PagedSeq(more, f, s, e) + } + + /** the subsequence from index `start' up to the + * length of the current sequence. + */ + override def slice(start: Int) = slice(start, UndeterminedEnd) + + /** Convert sequence to string */ + override def toString = { + val buf = new StringBuilder + for (ch <- elements) buf append ch + buf.toString + } +} + + +/** Page containing up to PageSize characters of the input sequence. + */ +private class Page[T](val num: Int) { + + private final val PageSize = 4096 + + /** The next page in the sequence */ + var next : Page[T] = null + + /** A later page in the sequence, serves a cachae for pointing to last page */ + var later : Page[T] = this + + /** The number of characters read into this page */ + var filled: Int = 0 + + /** Is this page the permamnently last one in the sequence? Only true once `more' + * method has returned -1 to signal end of input. */ + var isLast: Boolean = false + + /** The character array */ + final val data = new Array[T](PageSize) + + /** The index of the first character in this page relative to the whole sequence */ + final def start = num * PageSize + + /** The index of the character following the last charcater in this page relative + * to the whole sequence */ + final def end = start + filled + + /** The currently last page in the sequence; might change as more charcaters are appended */ + final def latest: Page[T] = { + if (later.next != null) later = later.next.latest + later + } + + /** The character at given sequence index. + * That index is relative to the whole sequence, not the page. */ + def apply(index: Int) = { + if (index < start || index - start >= filled) throw new IndexOutOfBoundsException(index.toString) + data(index - start) + } + + /** produces more characters by calling `more' and appends them on the current page, + * or fills a subsequent page if current page is full + * pre: if current page is full, it is the last one in the sequence. + */ + final def addMore(more: (Array[T], Int, Int) => Int): Page[T] = + if (filled == PageSize) { + next = new Page[T](num + 1) + next.addMore(more) + } else { + val count = more(data, filled, PageSize - filled) + if (count < 0) isLast = true + else filled += count + this + } +} diff --git a/src/library/scala/runtime/RichString.scala b/src/library/scala/runtime/RichString.scala index 192cf62ebb..412f2fd86c 100644 --- a/src/library/scala/runtime/RichString.scala +++ b/src/library/scala/runtime/RichString.scala @@ -14,7 +14,7 @@ package scala.runtime import Predef._ import scala.util.matching.Regex -final class RichString(val self: String) extends Proxy with CharSequence with RandomAccessSeq[Char] with Ordered[String] { +final class RichString(val self: String) extends Proxy with RandomAccessSeq[Char] with Ordered[String] { import RichString._ override def apply(n: Int) = self charAt n override def length = self.length @@ -74,11 +74,6 @@ final class RichString(val self: String) extends Proxy with CharSequence with Ra new RichString(buf.toString) } - def charAt(index: Int) = self.charAt(index) - - def subSequence(start: Int, end: Int): CharSequence = - new RichString(self.substring(start, end)) - /** return n times the current string */ def * (n: Int): String = { @@ -220,7 +215,7 @@ final class RichString(val self: String) extends Proxy with CharSequence with Ra def toFloat: Float = java.lang.Float.parseFloat(self) def toDouble: Double = java.lang.Double.parseDouble(self) - override def toArray: Array[Char] = { + def toArray: Array[Char] = { val result = new Array[Char](length) self.getChars(0, length, result, 0) result diff --git a/src/library/scala/util/parsing/combinator/RegexParsers.scala b/src/library/scala/util/parsing/combinator/RegexParsers.scala index 37b5f1d10c..49af37c8fc 100644 --- a/src/library/scala/util/parsing/combinator/RegexParsers.scala +++ b/src/library/scala/util/parsing/combinator/RegexParsers.scala @@ -13,6 +13,7 @@ package scala.util.parsing.combinator import java.util.regex.Pattern import scala.util.matching.Regex import scala.util.parsing.input._ +import scala.collection.immutable.PagedSeq trait RegexParsers extends Parsers { @@ -22,9 +23,9 @@ trait RegexParsers extends Parsers { def skipWhitespace = whiteSpace.toString.length > 0 - protected def handleWhiteSpace(source: CharSequence, offset: Int): Int = + protected def handleWhiteSpace(source: java.lang.CharSequence, offset: Int): Int = if (skipWhitespace) - (whiteSpace findPrefixMatchOf (source subSequence offset)) match { + (whiteSpace findPrefixMatchOf (source.subSequence(offset, source.length))) match { case Some(matched) => offset + matched.end case None => offset } @@ -39,7 +40,7 @@ trait RegexParsers extends Parsers { val start = handleWhiteSpace(source, offset) var i = 0 var j = start - while (i < s.length && source.isDefinedAt(j) && s.charAt(i) == source.charAt(j)) { + while (i < s.length && j < source.length && s.charAt(i) == source.charAt(j)) { i += 1 j += 1 } @@ -56,7 +57,7 @@ trait RegexParsers extends Parsers { val source = in.source val offset = in.offset val start = handleWhiteSpace(source, offset) - (r findPrefixMatchOf (source subSequence start)) match { + (r findPrefixMatchOf (source.subSequence(start, source.length))) match { case Some(matched) => Success(source.subSequence(start, start + matched.end).toString, in.drop(start + matched.end - offset)) @@ -66,19 +67,27 @@ trait RegexParsers extends Parsers { } } + /** Parse some prefix of reader `in' with parser `p' */ + def parse[T](p: Parser[T], in: Reader[Char]): ParseResult[T] = + p(in) + /** Parse some prefix of character sequence `in' with parser `p' */ - def parse[T](p: Parser[T], in: CharSequence): ParseResult[T] = + def parse[T](p: Parser[T], in: java.lang.CharSequence): ParseResult[T] = p(new CharSequenceReader(in)) /** Parse some prefix of reader `in' with parser `p' */ - def parse[T](p: Parser[T], in: Reader[Char]): ParseResult[T] = - p(in) + def parse[T](p: Parser[T], in: java.io.Reader): ParseResult[T] = + p(new PagedSeqReader(PagedSeq.fromReader(in))) - /** Parse all of character sequence `in' with parser `p' */ - def parseAll[T](p: Parser[T], in: CharSequence): ParseResult[T] = + /** Parse all of reader `in' with parser `p' */ + def parseAll[T](p: Parser[T], in: Reader[Char]): ParseResult[T] = parse(phrase(p), in) /** Parse all of reader `in' with parser `p' */ - def parseAll[T](p: Parser[T], in: Reader[Char]): ParseResult[T] = + def parseAll[T](p: Parser[T], in: java.io.Reader): ParseResult[T] = + parse(phrase(p), in) + + /** Parse all of character sequence `in' with parser `p' */ + def parseAll[T](p: Parser[T], in: java.lang.CharSequence): ParseResult[T] = parse(phrase(p), in) } diff --git a/src/library/scala/util/parsing/input/CharArrayReader.scala b/src/library/scala/util/parsing/input/CharArrayReader.scala index 44e1b6eb6e..e8eef0e78c 100644 --- a/src/library/scala/util/parsing/input/CharArrayReader.scala +++ b/src/library/scala/util/parsing/input/CharArrayReader.scala @@ -32,7 +32,7 @@ object CharArrayReader { * @author Martin Odersky, Adriaan Moors */ class CharArrayReader(chars: Array[Char], index: Int) -extends CharSequenceReader(CharSequence.fromArray(chars), index) { +extends CharSequenceReader(chars, index) { def this(chars: Array[Char]) = this(chars, 0) diff --git a/src/library/scala/util/parsing/input/CharSequenceReader.scala b/src/library/scala/util/parsing/input/CharSequenceReader.scala index bf1ad6e244..4b06808889 100644 --- a/src/library/scala/util/parsing/input/CharSequenceReader.scala +++ b/src/library/scala/util/parsing/input/CharSequenceReader.scala @@ -26,19 +26,19 @@ object CharSequenceReader { * * @author Martin Odersky */ -class CharSequenceReader(override val source: CharSequence, +class CharSequenceReader(override val source: java.lang.CharSequence, override val offset: Int) extends Reader[Char] { import CharSequenceReader._ /** Construct a <code>CharSequenceReader</code> with its first element at * <code>source(0)</code> and position <code>(1,1)</code>. */ - def this(source: CharSequence) = this(source, 0) + def this(source: java.lang.CharSequence) = this(source, 0) /** Returns the first element of the reader, or EofCh if reader is at its end */ def first = - if (source.isDefinedAt(offset)) source(offset) else EofCh + if (offset < source.length) source.charAt(offset) else EofCh /** Returns a CharSequenceReader consisting of all elements except the first * @@ -47,7 +47,7 @@ class CharSequenceReader(override val source: CharSequence, * the rest of input. */ def rest: CharSequenceReader = - if (source.isDefinedAt(offset)) new CharSequenceReader(source, offset + 1) + if (offset < source.length) new CharSequenceReader(source, offset + 1) else this /** The position of the first element in the reader @@ -57,7 +57,7 @@ class CharSequenceReader(override val source: CharSequence, /** true iff there are no more elements in this reader (except for trailing * EofCh's) */ - def atEnd = !source.isDefinedAt(offset) + def atEnd = offset >= source.length /** Returns an abstract reader consisting of all elements except the first * <code>n</code> elements. diff --git a/src/library/scala/util/parsing/input/PagedSeqReader.scala b/src/library/scala/util/parsing/input/PagedSeqReader.scala new file mode 100755 index 0000000000..f6139219ab --- /dev/null +++ b/src/library/scala/util/parsing/input/PagedSeqReader.scala @@ -0,0 +1,71 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2006-2007, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: PagedSeqReader.scala 14416 2008-03-19 01:17:25Z mihaylov $ + +package scala.util.parsing.input + +import scala.collection.immutable.PagedSeq + +/** An object encapsulating basic character constants + * + * @author Martin Odersky, Adriaan Moors + */ +object PagedSeqReader { + final val EofCh = '\032' +} + +/** A character array reader reads a stream of characters (keeping track of their positions) + * from an array. + * + * @param source the source sequence + * @param offset starting offset. + * + * @author Martin Odersky + */ +class PagedSeqReader(seq: PagedSeq[Char], + override val offset: Int) extends Reader[Char] { + import PagedSeqReader._ + + override lazy val source: java.lang.CharSequence = seq + + /** Construct a <code>PagedSeqReader</code> with its first element at + * <code>source(0)</code> and position <code>(1,1)</code>. + */ + def this(seq: PagedSeq[Char]) = this(seq, 0) + + /** Returns the first element of the reader, or EofCh if reader is at its end + */ + def first = + if (seq.isDefinedAt(offset)) seq(offset) else EofCh + + /** Returns a PagedSeqReader consisting of all elements except the first + * + * @return If <code>atEnd</code> is <code>true</code>, the result will be + * <code>this'; otherwise, it's a <code>PagedSeqReader</code> containing + * the rest of input. + */ + def rest: PagedSeqReader = + if (seq.isDefinedAt(offset)) new PagedSeqReader(seq, offset + 1) + else this + + /** The position of the first element in the reader + */ + def pos: Position = new OffsetPosition(source, offset) + + /** true iff there are no more elements in this reader (except for trailing + * EofCh's) + */ + def atEnd = !seq.isDefinedAt(offset) + + /** Returns an abstract reader consisting of all elements except the first + * <code>n</code> elements. + */ + override def drop(n: Int): PagedSeqReader = + new PagedSeqReader(seq, offset + n) +} diff --git a/src/library/scala/util/parsing/input/Reader.scala b/src/library/scala/util/parsing/input/Reader.scala index 51cf7eeb47..d46173e416 100644 --- a/src/library/scala/util/parsing/input/Reader.scala +++ b/src/library/scala/util/parsing/input/Reader.scala @@ -18,15 +18,14 @@ package scala.util.parsing.input */ abstract class Reader[+T] { - private[parsing] def source: CharSequence = this match { - case csr: CharSequenceReader => csr.source - case _ => throw new IllegalArgumentException("This kind of parser operates only on a CharSequenceReader") - } + /** If this is a reader over character sequences, the underlying char sequence + * If not, throws a <code>NoSuchMethodError</code> exception. + */ + def source: java.lang.CharSequence = + throw new NoSuchMethodError("not a char sequence reader") - private[parsing] def offset: Int = this match { - case csr: CharSequenceReader => csr.offset - case _ => throw new IllegalArgumentException("This kind of parser operates only on a CharSequenceReader") - } + def offset: Int = + throw new NoSuchMethodError("not a char sequence reader") /** Returns the first element of the reader */ diff --git a/src/library/scala/util/parsing/input/StreamReader.scala b/src/library/scala/util/parsing/input/StreamReader.scala index f997590f18..a6fa1a7bcc 100644 --- a/src/library/scala/util/parsing/input/StreamReader.scala +++ b/src/library/scala/util/parsing/input/StreamReader.scala @@ -11,6 +11,7 @@ package scala.util.parsing.input import java.io.BufferedReader +import scala.collection.immutable.PagedSeq /** An object to create a StreamReader from a <code>java.io.Reader</code>. * @@ -23,52 +24,52 @@ object StreamReader { final val EofCh = '\032' def apply(in: java.io.Reader): StreamReader = { - new StreamReader(new LazyCharSequence(in), 0, 1) + new StreamReader(PagedSeq.fromReader(in), 0, 1) } } -/** A character array reader reads a stream of characters (keeping track of - * their positions) from an array. +/** A StreamReader reads from a character sequence, typically created as a PagedSeq + * from a java.io.Reader * * NOTE: * StreamReaders do not really fulfill the new contract for readers, which * requires a `source' CharSequence representing the full input. * Instead source is treated line by line. - * As a consequence, regex matching cannot extend beyond a single lines + * As a consequence, regex matching cannot extend beyond a single line * when a StreamReader are used for input. * - * @param bin the underlying java.io.BufferedReader - * @param sourceLine the line at column `col' in the stream - * @param line the 1-based line number of the character returned by `first' - * @param column the 1-based column number of the character returned by `first' + * If you need to match regexes spanning several lines you should consider + * class <code>PagedSeqReader</code> instead. * - * @author Miles Sabin + * @author Miles Sabin + * @author Martin Odersky */ -sealed class StreamReader(source: CharSequence, offset: Int, lnum: Int) extends CharSequenceReader(source, offset) { +sealed class StreamReader(seq: PagedSeq[Char], off: Int, lnum: Int) extends PagedSeqReader(seq, off) { import StreamReader._ - override def rest: CharSequenceReader = - if (offset == source.length) this - else if (source(offset) == '\n') new StreamReader(source.subSequence(offset + 1), 0, lnum + 1) - else new StreamReader(source, offset + 1, lnum) + override def rest: StreamReader = + if (off == seq.length) this + else if (seq(off) == '\n') + new StreamReader(seq.slice(off + 1), 0, lnum + 1) + else new StreamReader(seq, off + 1, lnum) private def nextEol = { - var i = offset - while (i < source.length && source(i) != '\n' && source(i) != EofCh) i += 1 + var i = off + while (i < seq.length && seq(i) != '\n' && seq(i) != EofCh) i += 1 i } override def drop(n: Int): StreamReader = { val eolPos = nextEol - if (eolPos < offset + n && eolPos < source.length) - new StreamReader(source.subSequence(eolPos + 1), 0, lnum + 1).drop(offset + n - (eolPos + 1)) + if (eolPos < off + n && eolPos < seq.length) + new StreamReader(seq.slice(eolPos + 1), 0, lnum + 1).drop(off + n - (eolPos + 1)) else - new StreamReader(source, offset + n, lnum) + new StreamReader(seq, off + n, lnum) } override def pos: Position = new Position { def line = lnum - def column = offset + 1 - def lineContents = source.subSequence(0, nextEol).toString + def column = off + 1 + def lineContents = seq.slice(0, nextEol).toString } } diff --git a/test/files/neg/bug414.check b/test/files/neg/bug414.check index 3396a803f1..ec23e26337 100644 --- a/test/files/neg/bug414.check +++ b/test/files/neg/bug414.check @@ -1,4 +1,4 @@ -bug414.scala:5: error: pattern type is incompatibe with expected type; +bug414.scala:5: error: pattern type is incompatible with expected type; found : object Empty required: IntMap[a] case Empty => diff --git a/test/files/run/caseclasses.scala b/test/files/run/caseclasses.scala index b971cf3582..3afd7ee162 100644 --- a/test/files/run/caseclasses.scala +++ b/test/files/run/caseclasses.scala @@ -4,6 +4,9 @@ case class Bar case class Baz(override val x: Int, y: Int) extends Foo(x)(y) +abstract class Base +abstract case class Abs(x: Int) extends Base + object M { abstract case class C(x: String) {} object C extends (String => C) { @@ -16,6 +19,11 @@ object M { object Test extends Application { + def Abs(x: Int) = new Abs(x * 2){} + Abs(2) match { + case Abs(4) => ; + } + def fn[a,b](x: a => b) = x; val f = fn(Foo(1)) (f(2): AnyRef) match { @@ -46,3 +54,4 @@ object Test extends Application { } } + |