From af47e5b433ea538bf096a176c88f3c91116e09cd Mon Sep 17 00:00:00 2001 From: Antonio Cunei Date: Tue, 25 Nov 2008 18:05:48 +0000 Subject: Merging everything from the 2.8.x development b... Merging everything from the 2.8.x development branch back to trunk. - If you were working on trunk, please keep working on trunk If you were - working on 2.8-devel, please switch to trunk now --- SIP/compiler-phase-init/sip-00002-1.dia | Bin 0 -> 1608 bytes SIP/compiler-phase-init/sip-00002-1.png | Bin 0 -> 17837 bytes SIP/compiler-phase-init/sip-00002-10.scala | 521 +++++++ SIP/compiler-phase-init/sip-00002-11.png | Bin 0 -> 74812 bytes SIP/compiler-phase-init/sip-00002-12.png | Bin 0 -> 70866 bytes SIP/compiler-phase-init/sip-00002-13.png | Bin 0 -> 70866 bytes SIP/compiler-phase-init/sip-00002-14.png | Bin 0 -> 66249 bytes SIP/compiler-phase-init/sip-00002-15.dia | Bin 0 -> 1266 bytes SIP/compiler-phase-init/sip-00002-15.png | Bin 0 -> 4547 bytes SIP/compiler-phase-init/sip-00002-16.dia | Bin 0 -> 1184 bytes SIP/compiler-phase-init/sip-00002-16.png | Bin 0 -> 3525 bytes SIP/compiler-phase-init/sip-00002-17.dia | Bin 0 -> 1550 bytes SIP/compiler-phase-init/sip-00002-17.png | Bin 0 -> 14670 bytes SIP/compiler-phase-init/sip-00002-18.dia | Bin 0 -> 1984 bytes SIP/compiler-phase-init/sip-00002-18.png | Bin 0 -> 24092 bytes SIP/compiler-phase-init/sip-00002-19.dia | Bin 0 -> 2267 bytes SIP/compiler-phase-init/sip-00002-19.png | Bin 0 -> 27719 bytes SIP/compiler-phase-init/sip-00002-2.dia | Bin 0 -> 1872 bytes SIP/compiler-phase-init/sip-00002-2.png | Bin 0 -> 28963 bytes SIP/compiler-phase-init/sip-00002-20.dia | Bin 0 -> 1709 bytes SIP/compiler-phase-init/sip-00002-20.png | Bin 0 -> 19697 bytes SIP/compiler-phase-init/sip-00002-21.dia | Bin 0 -> 2226 bytes SIP/compiler-phase-init/sip-00002-21.png | Bin 0 -> 28240 bytes SIP/compiler-phase-init/sip-00002-22.dia | Bin 0 -> 1572 bytes SIP/compiler-phase-init/sip-00002-22.png | Bin 0 -> 21025 bytes SIP/compiler-phase-init/sip-00002-23.dia | Bin 0 -> 2036 bytes SIP/compiler-phase-init/sip-00002-23.png | Bin 0 -> 33568 bytes SIP/compiler-phase-init/sip-00002-24.dia | Bin 0 -> 2335 bytes SIP/compiler-phase-init/sip-00002-24.png | Bin 0 -> 69413 bytes SIP/compiler-phase-init/sip-00002-3.dia | Bin 0 -> 2108 bytes SIP/compiler-phase-init/sip-00002-3.png | Bin 0 -> 33475 bytes SIP/compiler-phase-init/sip-00002-4.dia | Bin 0 -> 2458 bytes SIP/compiler-phase-init/sip-00002-4.png | Bin 0 -> 43127 bytes SIP/compiler-phase-init/sip-00002-5.dia | Bin 0 -> 3092 bytes SIP/compiler-phase-init/sip-00002-5.png | Bin 0 -> 52198 bytes SIP/compiler-phase-init/sip-00002-6.dia | Bin 0 -> 2535 bytes SIP/compiler-phase-init/sip-00002-6.png | Bin 0 -> 75908 bytes SIP/compiler-phase-init/sip-00002-7.dia | Bin 0 -> 1849 bytes SIP/compiler-phase-init/sip-00002-7.png | Bin 0 -> 25512 bytes SIP/compiler-phase-init/sip-00002-8.dia | Bin 0 -> 2301 bytes SIP/compiler-phase-init/sip-00002-8.png | Bin 0 -> 40297 bytes SIP/compiler-phase-init/sip-00002-9.dia | Bin 0 -> 2924 bytes SIP/compiler-phase-init/sip-00002-9.png | Bin 0 -> 20512 bytes SIP/compiler-phase-init/sip.xhtml | 1546 ++++++++++++++++++++ SIP/presip-classic.xhtml | 29 + SIP/presip-constraints.xhtml | 29 + SIP/presip-depmethods.xhtml | 29 + SIP/sip.css | 5 + SIP/virtual-traits/sip-0000X-1.xhtml | 100 ++ SIP/virtual-traits/sip-0000X.xhtml | 341 +++++ build.number | 6 +- lib/scala-compiler.jar.desired.sha1 | 2 +- lib/scala-library-src.jar.desired.sha1 | 2 +- lib/scala-library.jar.desired.sha1 | 2 +- src/compiler/scala/tools/nsc/Settings.scala | 1 + .../scala/tools/nsc/ast/parser/Parsers.scala | 48 +- .../scala/tools/nsc/symtab/Definitions.scala | 2 +- src/compiler/scala/tools/nsc/symtab/Flags.scala | 149 +- src/compiler/scala/tools/nsc/symtab/Types.scala | 1 + .../scala/tools/nsc/symtab/classfile/Pickler.scala | 9 +- .../tools/nsc/symtab/classfile/UnPickler.scala | 9 +- .../scala/tools/nsc/typechecker/Namers.scala | 8 +- .../scala/tools/nsc/typechecker/RefChecks.scala | 9 +- .../scala/tools/nsc/typechecker/Typers.scala | 9 +- .../scala/tools/nsc/util/ShowPickled.scala | 8 +- .../annotation/unchecked/uncheckedStable.scala | 13 + .../annotation/unchecked/uncheckedVariance.scala | 2 +- .../scala/collection/mutable/ArrayBuffer.scala | 2 +- .../scala/collection/mutable/ResizableArray.scala | 4 +- src/library/scalax/Fractional.scala | 5 + src/library/scalax/Integral.scala | 6 + src/library/scalax/Numeric.scala | 79 + .../scalax/collection/BufferedIterator.scala | 27 + src/library/scalax/collection/Builder.scala | 21 + src/library/scalax/collection/Iterable.scala | 143 ++ src/library/scalax/collection/Iterator.scala | 970 ++++++++++++ .../scalax/collection/OrderedIterable.scala | 41 + src/library/scalax/collection/Sequence.scala | 45 + src/library/scalax/collection/SizedIterable.scala | 38 + src/library/scalax/collection/Vector.scala | 21 + .../collection/generic/IterableFactory.scala | 108 ++ .../collection/generic/IterableForwarder.scala | 61 + .../collection/generic/IterableTemplate.scala | 816 +++++++++++ .../scalax/collection/generic/IterableView.scala | 121 ++ .../generic/OrderedIterableTemplate.scala | 23 + .../collection/generic/SequenceFactory.scala | 11 + .../collection/generic/SequenceForwarder.scala | 50 + .../collection/generic/SequenceTemplate.scala | 382 +++++ .../scalax/collection/generic/SequenceView.scala | 129 ++ .../scalax/collection/generic/VectorTemplate.scala | 264 ++++ .../generic/covariant/IterableFactory.scala | 14 + .../generic/covariant/IterableTemplate.scala | 29 + .../generic/covariant/IterableView.scala | 21 + .../covariant/OrderedIterableTemplate.scala | 17 + .../generic/covariant/SequenceFactory.scala | 3 + .../generic/covariant/SequenceTemplate.scala | 17 + .../generic/covariant/SequenceView.scala | 22 + .../generic/covariant/VectorTemplate.scala | 17 + .../generic/covartest/IterableFactory.scala | 108 ++ .../generic/covartest/IterableTemplate.scala | 816 +++++++++++ .../generic/covartest/IterableView.scala | 121 ++ .../covartest/OrderedIterableTemplate.scala | 17 + .../generic/covartest/SequenceFactory.scala | 11 + .../generic/covartest/SequenceTemplate.scala | 382 +++++ .../generic/covartest/SequenceView.scala | 129 ++ .../generic/covartest/VectorTemplate.scala | 264 ++++ .../generic/mutable/VectorTemplate.scala | 55 + .../collection/generic/mutable/VectorView.scala | 95 ++ .../generic/nonvariant/IterableFactory.scala | 3 + .../generic/nonvariant/IterableTemplate.scala | 24 + .../generic/nonvariant/IterableView.scala | 19 + .../nonvariant/OrderedIterableTemplate.scala | 15 + .../generic/nonvariant/SequenceFactory.scala | 3 + .../generic/nonvariant/SequenceTemplate.scala | 15 + .../generic/nonvariant/SequenceView.scala | 21 + .../generic/nonvariant/VectorTemplate.scala | 15 + src/library/scalax/collection/immutable/List.scala | 1222 ++++++++++++++++ .../scalax/collection/mutable/Appendable.scala | 94 ++ .../scalax/collection/mutable/ArrayBuffer.scala | 118 ++ src/library/scalax/collection/mutable/Buffer.scala | 264 ++++ .../collection/mutable/CloneableCollection.scala | 19 + .../scalax/collection/mutable/ListBuffer.scala | 288 ++++ .../scalax/collection/mutable/ResizableArray.scala | 103 ++ src/library/scalax/collection/mutable/Vector.scala | 20 + src/library/scalax/util/control/Break.scala | 15 + test/files/neg/accesses.check | 8 +- test/files/neg/bug521.check | 4 +- test/files/neg/bug630.check | 4 +- test/files/neg/bug708.check | 4 +- test/files/neg/lazy-override.check | 4 +- test/files/neg/t1163.check | 4 +- test/files/neg/tcpoly_variance.check | 4 +- test/files/run/t1524.check | 1 + test/files/run/t1524.scala | 7 + 134 files changed, 10593 insertions(+), 90 deletions(-) create mode 100644 SIP/compiler-phase-init/sip-00002-1.dia create mode 100644 SIP/compiler-phase-init/sip-00002-1.png create mode 100644 SIP/compiler-phase-init/sip-00002-10.scala create mode 100644 SIP/compiler-phase-init/sip-00002-11.png create mode 100644 SIP/compiler-phase-init/sip-00002-12.png create mode 100644 SIP/compiler-phase-init/sip-00002-13.png create mode 100644 SIP/compiler-phase-init/sip-00002-14.png create mode 100644 SIP/compiler-phase-init/sip-00002-15.dia create mode 100644 SIP/compiler-phase-init/sip-00002-15.png create mode 100644 SIP/compiler-phase-init/sip-00002-16.dia create mode 100644 SIP/compiler-phase-init/sip-00002-16.png create mode 100644 SIP/compiler-phase-init/sip-00002-17.dia create mode 100644 SIP/compiler-phase-init/sip-00002-17.png create mode 100644 SIP/compiler-phase-init/sip-00002-18.dia create mode 100644 SIP/compiler-phase-init/sip-00002-18.png create mode 100644 SIP/compiler-phase-init/sip-00002-19.dia create mode 100644 SIP/compiler-phase-init/sip-00002-19.png create mode 100644 SIP/compiler-phase-init/sip-00002-2.dia create mode 100644 SIP/compiler-phase-init/sip-00002-2.png create mode 100644 SIP/compiler-phase-init/sip-00002-20.dia create mode 100644 SIP/compiler-phase-init/sip-00002-20.png create mode 100644 SIP/compiler-phase-init/sip-00002-21.dia create mode 100644 SIP/compiler-phase-init/sip-00002-21.png create mode 100644 SIP/compiler-phase-init/sip-00002-22.dia create mode 100644 SIP/compiler-phase-init/sip-00002-22.png create mode 100644 SIP/compiler-phase-init/sip-00002-23.dia create mode 100644 SIP/compiler-phase-init/sip-00002-23.png create mode 100644 SIP/compiler-phase-init/sip-00002-24.dia create mode 100644 SIP/compiler-phase-init/sip-00002-24.png create mode 100644 SIP/compiler-phase-init/sip-00002-3.dia create mode 100644 SIP/compiler-phase-init/sip-00002-3.png create mode 100644 SIP/compiler-phase-init/sip-00002-4.dia create mode 100644 SIP/compiler-phase-init/sip-00002-4.png create mode 100644 SIP/compiler-phase-init/sip-00002-5.dia create mode 100644 SIP/compiler-phase-init/sip-00002-5.png create mode 100644 SIP/compiler-phase-init/sip-00002-6.dia create mode 100644 SIP/compiler-phase-init/sip-00002-6.png create mode 100644 SIP/compiler-phase-init/sip-00002-7.dia create mode 100644 SIP/compiler-phase-init/sip-00002-7.png create mode 100644 SIP/compiler-phase-init/sip-00002-8.dia create mode 100644 SIP/compiler-phase-init/sip-00002-8.png create mode 100644 SIP/compiler-phase-init/sip-00002-9.dia create mode 100644 SIP/compiler-phase-init/sip-00002-9.png create mode 100644 SIP/compiler-phase-init/sip.xhtml create mode 100644 SIP/presip-classic.xhtml create mode 100644 SIP/presip-constraints.xhtml create mode 100644 SIP/presip-depmethods.xhtml create mode 100644 SIP/sip.css create mode 100644 SIP/virtual-traits/sip-0000X-1.xhtml create mode 100644 SIP/virtual-traits/sip-0000X.xhtml create mode 100755 src/library/scala/annotation/unchecked/uncheckedStable.scala create mode 100755 src/library/scalax/Fractional.scala create mode 100755 src/library/scalax/Integral.scala create mode 100755 src/library/scalax/Numeric.scala create mode 100755 src/library/scalax/collection/BufferedIterator.scala create mode 100755 src/library/scalax/collection/Builder.scala create mode 100755 src/library/scalax/collection/Iterable.scala create mode 100755 src/library/scalax/collection/Iterator.scala create mode 100755 src/library/scalax/collection/OrderedIterable.scala create mode 100755 src/library/scalax/collection/Sequence.scala create mode 100644 src/library/scalax/collection/SizedIterable.scala create mode 100755 src/library/scalax/collection/Vector.scala create mode 100755 src/library/scalax/collection/generic/IterableFactory.scala create mode 100644 src/library/scalax/collection/generic/IterableForwarder.scala create mode 100755 src/library/scalax/collection/generic/IterableTemplate.scala create mode 100755 src/library/scalax/collection/generic/IterableView.scala create mode 100755 src/library/scalax/collection/generic/OrderedIterableTemplate.scala create mode 100755 src/library/scalax/collection/generic/SequenceFactory.scala create mode 100644 src/library/scalax/collection/generic/SequenceForwarder.scala create mode 100755 src/library/scalax/collection/generic/SequenceTemplate.scala create mode 100755 src/library/scalax/collection/generic/SequenceView.scala create mode 100644 src/library/scalax/collection/generic/VectorTemplate.scala create mode 100755 src/library/scalax/collection/generic/covariant/IterableFactory.scala create mode 100755 src/library/scalax/collection/generic/covariant/IterableTemplate.scala create mode 100755 src/library/scalax/collection/generic/covariant/IterableView.scala create mode 100755 src/library/scalax/collection/generic/covariant/OrderedIterableTemplate.scala create mode 100755 src/library/scalax/collection/generic/covariant/SequenceFactory.scala create mode 100755 src/library/scalax/collection/generic/covariant/SequenceTemplate.scala create mode 100755 src/library/scalax/collection/generic/covariant/SequenceView.scala create mode 100644 src/library/scalax/collection/generic/covariant/VectorTemplate.scala create mode 100755 src/library/scalax/collection/generic/covartest/IterableFactory.scala create mode 100755 src/library/scalax/collection/generic/covartest/IterableTemplate.scala create mode 100755 src/library/scalax/collection/generic/covartest/IterableView.scala create mode 100755 src/library/scalax/collection/generic/covartest/OrderedIterableTemplate.scala create mode 100755 src/library/scalax/collection/generic/covartest/SequenceFactory.scala create mode 100755 src/library/scalax/collection/generic/covartest/SequenceTemplate.scala create mode 100755 src/library/scalax/collection/generic/covartest/SequenceView.scala create mode 100644 src/library/scalax/collection/generic/covartest/VectorTemplate.scala create mode 100644 src/library/scalax/collection/generic/mutable/VectorTemplate.scala create mode 100644 src/library/scalax/collection/generic/mutable/VectorView.scala create mode 100755 src/library/scalax/collection/generic/nonvariant/IterableFactory.scala create mode 100755 src/library/scalax/collection/generic/nonvariant/IterableTemplate.scala create mode 100755 src/library/scalax/collection/generic/nonvariant/IterableView.scala create mode 100755 src/library/scalax/collection/generic/nonvariant/OrderedIterableTemplate.scala create mode 100755 src/library/scalax/collection/generic/nonvariant/SequenceFactory.scala create mode 100755 src/library/scalax/collection/generic/nonvariant/SequenceTemplate.scala create mode 100755 src/library/scalax/collection/generic/nonvariant/SequenceView.scala create mode 100644 src/library/scalax/collection/generic/nonvariant/VectorTemplate.scala create mode 100644 src/library/scalax/collection/immutable/List.scala create mode 100644 src/library/scalax/collection/mutable/Appendable.scala create mode 100644 src/library/scalax/collection/mutable/ArrayBuffer.scala create mode 100644 src/library/scalax/collection/mutable/Buffer.scala create mode 100644 src/library/scalax/collection/mutable/CloneableCollection.scala create mode 100644 src/library/scalax/collection/mutable/ListBuffer.scala create mode 100644 src/library/scalax/collection/mutable/ResizableArray.scala create mode 100644 src/library/scalax/collection/mutable/Vector.scala create mode 100755 src/library/scalax/util/control/Break.scala create mode 100644 test/files/run/t1524.check create mode 100644 test/files/run/t1524.scala diff --git a/SIP/compiler-phase-init/sip-00002-1.dia b/SIP/compiler-phase-init/sip-00002-1.dia new file mode 100644 index 0000000000..94e4ffa3b3 Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-1.dia differ diff --git a/SIP/compiler-phase-init/sip-00002-1.png b/SIP/compiler-phase-init/sip-00002-1.png new file mode 100644 index 0000000000..f4bbf67e79 Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-1.png differ diff --git a/SIP/compiler-phase-init/sip-00002-10.scala b/SIP/compiler-phase-init/sip-00002-10.scala new file mode 100644 index 0000000000..2065a7035d --- /dev/null +++ b/SIP/compiler-phase-init/sip-00002-10.scala @@ -0,0 +1,521 @@ +/* Package information */ +package depgraph + + +/* Simple import of data structures and IO */ +import scala.collection.mutable.{HashSet,HashMap} +import java.io.{BufferedWriter,FileWriter} + + + +/* Class made to model the internal compiler phases + * + * All internal phases inherits from SubComponent + */ +abstract class SubComponent { + val phaseName: String + val runsAfter: List[String] + val runsRightAfter: Option[String] + val internal: Boolean = true +} + +/* + * + */ +class Global extends Plugins with PhaseAssembly { + + /* Simple option value to hold the compiler phase chain */ + private var phasesCache : Option[List[SubComponent]] = None + + /* The set of phase objects that is the basis for the compiler phase chain */ + protected val phasesSet : HashSet[SubComponent] = new HashSet[SubComponent] + + /* All the internal phase objects + * + */ + object parser extends { + val phaseName = "nsc::parser" + val runsAfter = List[String]() + val runsRightAfter = None + } with SubComponent + + object typer extends { + val phaseName = "nsc::typer" + val runsAfter = List[String]("nsc::parser") + val runsRightAfter = None + } with SubComponent + + object pickler extends { + val phaseName = "nsc::pickler" + val runsAfter = List[String]("nsc::typer") + val runsRightAfter = None + } with SubComponent + + object liftcode extends { + val phaseName = "nsc::liftcode" + val runsAfter = List[String]("nsc::pickler") + val runsRightAfter = None + } with SubComponent + + object tailcalls extends { + val phaseName = "nsc::tailcalls" + val runsAfter = List[String]("nsc::pickler","nsc::liftcode") + val runsRightAfter = None + } with SubComponent + + object erasure extends { + val phaseName = "nsc::erasure" + val runsAfter = List[String]() + val runsRightAfter = Some("nsc::tailcalls") + } with SubComponent + + object cleanup extends { + val phaseName = "nsc::cleanup" + val runsAfter = List[String]("nsc::erasure") + val runsRightAfter = None + } with SubComponent + + object jvm extends { + val phaseName = "nsc::jvm" + val runsAfter = List[String]("nsc::cleanup") + val runsRightAfter = None + } with SubComponent + + object terminal extends { + val phaseName = "nsc::terminal" + val runsAfter = List[String]("nsc::jvm","nsc::msil") + val runsRightAfter = None + } with SubComponent + + /* Helper method + */ + private def computePhaseDescriptors: List[SubComponent] = { + computeInternalPhases() // Global.scala + computePluginPhases() // plugins/Plugins.scala + buildCompilerFromPhasesSet() // PhaseAssembly.scala + } + + /* Will add the internal compiler phases to the phases set + */ + protected def computeInternalPhases() { + phasesSet += parser + phasesSet += typer + phasesSet += pickler + phasesSet += liftcode + phasesSet += tailcalls + phasesSet += erasure + phasesSet += cleanup + phasesSet += jvm + phasesSet += terminal + } + + /* Getter method for the compiler phases chain + */ + def phaseDescriptors : List[SubComponent] = { + if (phasesCache.isEmpty) { + phasesCache = Some(computePhaseDescriptors) + } + phasesCache.get + } + +} + + +/* Class make to model the Plug-in supplied phases + * + * All external phases inherits from PluginComponent + */ +abstract class PluginComponent extends SubComponent { + override val internal = false + val runsRightAfter: Option[String] = None +} + +/* Trait made to model the behavior of the plugins + * + */ +trait Plugins { self: Global => + + /* Example plugin phases + * + */ + object plugin1 extends { + val phaseName = "plug1::optimization1" + val runsAfter = List[String]("nsc::typer") + } with PluginComponent + + object plugin2 extends { + val phaseName = "plug2::optimization1" + val runsAfter = List[String]("nsc::liftcode") + } with PluginComponent + + object plugin3 extends { + val phaseName = "plug2::optimization2" + val runsAfter = List[String]("plug2::optimization1","nsc::cleanup") + } with PluginComponent + + /* Add plug-in supplied phase objects to the phases set + */ + def computePluginPhases() { + phasesSet += plugin1 + phasesSet += plugin2 + phasesSet += plugin3 + } + +} + + +/* Trait made to seperate the constraint solving from the rest of the compiler + * + */ +trait PhaseAssembly { self: Global => + + /* Aux datastructure for solving the constraint system + * Simple edge with to and from refs + */ + class Edge(f: Node, t: Node, h: Boolean) { + var frm = f + var to = t + var hard = h + } + + /* Aux datastructure for solving the constraint system + * Simple node with name and object ref for the phase object, + * also sets of in and out going dependencies + */ + class Node(phs:SubComponent, name:String) { + var phasename: String = name + var phaseobj: SubComponent = phs + var after: HashSet[Edge] = new HashSet[Edge]() + var deps: HashSet[Edge] = new HashSet[Edge]() + } + + /* Aux datastructure for solving the constraint system + * The depency graph container with helper methods for node and edge creation + */ + class DependencyGraph { + + val nodes = new HashMap[String,Node]() + val edges = new HashSet[Edge]() + + /* Given a phase object, get the node for this phase object. If the + * node object does not exist, then create it. + */ + def getNodeByPhase(phs : SubComponent) : Node = { + var node : Node = getNodeByPhase(phs.phaseName) + if (node.phaseobj == null) { + node.phaseobj = phs + } + node + } + + /* Given the name of a phase object, get the node for that name. If the + * node object does not exits, then create it. + */ + def getNodeByPhase(name : String) : Node = { + var node : Node = null + this.nodes.get(name) match { + case None => + node = new Node(null,name) + nodes += (name->node) + case Some(n) => + node = n + } + node + } + + /* Connect the frm and to nodes with an edge and make it soft. + * Also add the edge object to the set of edges, and to the dependency + * list of the nodes + */ + def softConnectNodes(frm: Node, to: Node) { + var e = new Edge(frm, to, false) + this.edges += e + + frm.after += e + to.deps += e + } + + /* Connect the frm and to nodes with an edge and make it hard. + * Also add the edge object to the set of edges, and to the dependency + * list of the nodes + */ + def hardConnectNodes(frm: Node, to: Node) { + var e = new Edge(frm, to, true) + this.edges += e + + frm.after += e + to.deps += e + } + } + + /* This method will simplify the graph, by removing unneeded edges and turning the graph into + * a tree. + */ + private def simplifyGraphFromNode(node : Node, graph : DependencyGraph) : Unit = { + var removal : List[Edge] = Nil + for(edge <- node.deps) { + if (edge.frm.after.size > 1) + removal = edge :: removal + } + for(edge <- removal) { + println("[removing edge: " + edge.frm.phasename + " => " + edge.to.phasename + "]") + node.deps -= edge + edge.frm.after -= edge + graph.edges -= edge + } + + var nodes = dependencyOrder(node.deps) + for(nd <- nodes) { + simplifyGraphFromNode(nd, graph) + } + } + + /* This is a simple method that tests for cycles in the graph. If a cycle is found, a fatal error + * will be produced. + */ + private def testForCycles(node : Node, names : HashSet[String]) : Unit = { + + if (names.contains( node.phasename ) ) { + println("There is a cycle in this graph! The algorithm was able to reach the node " + node.phasename + " twice!") + System.exit(1) + } + + names += node.phasename + + var nodes = dependencyOrder(node.deps) + for(nd <- nodes) { + testForCycles(nd, names) + } + + names -= node.phasename + } + + /* Given the dependency list of a node, return a list so that first come the reversed + * external phases sorted alphabetically, followed by the internal phase + */ + private def dependencyOrder(deps : HashSet[Edge]) : List[Node] = { + var external = deps.filter(e => (! e.frm.phaseobj.internal)) + var internal = deps.filter(e => e.frm.phaseobj.internal) + + var extnodes = (Nil ++ external.map(e => e.frm)).sort((n1,n2) => (n1.phasename compareTo n2.phasename) < 0) + extnodes = extnodes.reverse + + var nodes = Nil ++ internal.map(e => e.frm) ++ extnodes + nodes = nodes.reverse + return nodes + } + + /* Find all edges in the given graph that are hard links. For each hard link we + * need to check that its the only dependency. If not, then we will promote the + * other dependencies down + */ + private def enforceHardlinks(graph : DependencyGraph) : Unit = { + var rerun = true + while(rerun) { + rerun = false + var hardlinks = graph.edges.filter(e => e.hard) + for(hl <- hardlinks) { + var sanity = Nil ++ hl.to.deps.filter(e => e.hard) + if (sanity.length == 0) { + + println("This is not supposed to happen!") + System.exit(1) + // throw new FataError() + } else if (sanity.length > 1) { + + println("Multiple phases want to run right after the same phase") + println("Phases:") + for (edge <- sanity) { + println(" - " + edge.frm.phasename) + } + System.exit(1) + + } else { + + var promote = hl.to.deps.filter(e => (!e.hard)) + hl.to.deps.clear + sanity foreach (edge => hl.to.deps += edge) + for (edge <- promote) { + rerun = true + println("[promote the dependency of " + edge.frm.phasename + ": " + edge.to.phasename + " => " + hl.frm.phasename + "]") + edge.to = hl.frm + hl.frm.deps += edge + } + } + } + } + } + + /* Remove all nodes in the given graph, that have no phase object + * Make sure to clean up all edges when removing the node object + */ + private def removeDanglingNodes(graph : DependencyGraph) : Unit = { + var dnodes = graph.nodes.values.filter(n => (n.phaseobj == null)) + for(node <- dnodes) { + println("[dropping depend on node with no phase: " + node.phasename + "]") + graph.nodes -= node.phasename + for(edge <- node.deps) { + graph.edges -= edge + edge.frm.after -= edge + } + } + } + + /* This is a helper method, that given a dependency graph will generate a graphviz dot + * file showing its structure. + * Plug-in supplied phases are marked as green nodes and hard links are marked as blue edges. + */ + private def graphToDotFile(graph : DependencyGraph, filename : String) : Unit = { + var sbuf = new StringBuffer() + var extnodes = new HashSet[Node]() + sbuf.append("digraph G {\n") + for(edge <- graph.edges) { + sbuf.append("\"" + edge.frm.phasename + "\"->\"" + edge.to.phasename + "\"") + if (! edge.frm.phaseobj.internal) { + extnodes += edge.frm + } + if (edge.hard) { + sbuf.append(" [color=\"#0000ff\"]\n") + } else { + sbuf.append(" [color=\"#000000\"]\n") + } + } + for(node <- extnodes) { + sbuf.append("\"" + node.phasename + "\" [color=\"#00ff00\"]\n") + } + sbuf.append("}\n") + var out = new BufferedWriter(new FileWriter(filename)) + out.write(sbuf.toString) + out.flush() + out.close() + } + + + /* Given the phases set, will build a dependency graph from the phases set + * Using the aux. method of the DependencyGraph to create nodes and egdes + */ + private def phasesSetToDepGraph(phsSet : HashSet[SubComponent]) : DependencyGraph = { + val graph = new DependencyGraph() + + for(phs <- phsSet) { + + var fromnode = graph.getNodeByPhase(phs) + + phs.runsRightAfter match { + case None => + for(phsname <- phs.runsAfter) { + if (! (phsname equals "terminal")) { + var tonode = graph.getNodeByPhase(phsname) + graph.softConnectNodes(fromnode, tonode) + } else { + println("[depends on terminal not allowed, dropping depend: " + fromnode.phasename + " => "+ phsname +"]") + } + } + case Some(phsname) => + if (! (phsname equals "terminal")) { + var tonode = graph.getNodeByPhase(phsname) + graph.hardConnectNodes(fromnode, tonode) + } else { + println("[depends on terminal not allowed, dropping depend: " + fromnode.phasename + " => "+ phsname +"]") + } + } + + } + + return graph + } + + + /* Simple transformation function, that given a dependency graph transforms it into a dependency tree + * Will return the root of the tree + */ + private def depGraphToDepTree(graph : DependencyGraph) : Node = { + + graphToDotFile(graph, "depgraph1.dot") + + // Remove nodes without phaseobj + removeDanglingNodes(graph) + + graphToDotFile(graph, "depgraph2.dot") + + // Enforce hardlinks / runsRightAfter and promote nodes down the tree + enforceHardlinks(graph) + + graphToDotFile(graph, "depgraph3.dot") + + var root = graph.getNodeByPhase("nsc::parser") + + // Test for cycles in graph, if found will generate fatal error + testForCycles(root, new HashSet[String]()) + + // Simplify graph by removing edges starting from the root + simplifyGraphFromNode(root, graph) + + graphToDotFile(graph, "depgraph4.dot") + + root + } + + + /* Given a node and a list of phases, it will traverse the dependencies of the node object and + * call itself recursively + * + */ + private def depTree2CompilerPhaseList(node : Node, pchain : List[SubComponent]) : List[SubComponent] = { + var chain : List[SubComponent] = pchain + chain = chain ::: List(node.phaseobj) + if (node.deps.size == 0) + return chain + + else if (node.deps.size == 1) { + for(edge <- node.deps) + chain = depTree2CompilerPhaseList(edge.frm, chain) + return chain + + } else { + + var nodes = dependencyOrder(node.deps) + for(nd <- nodes) { + chain = depTree2CompilerPhaseList(nd, chain) + } + return chain + } + } + + + /* Method called from computePhaseDescriptors in class Global + * This method will call three aux. methods to convert the phases set into a dependency graph + * Convert the dependency graph into a dependency tree and return the root of the tree + * Assemble the compiler phase chain from the root of the dependency tree + */ + def buildCompilerFromPhasesSet() : List[SubComponent] = { + + // Add all phases in the set to the graph + val graph = phasesSetToDepGraph(phasesSet) + + val root = depGraphToDepTree(graph) + + return depTree2CompilerPhaseList(root, Nil) + } + +} + + +/** + * Test object that will create a new object from the Global class + * and call the method phaseDescriptors to get the list of phase objects + * and print the phase names to stdout + * + */ +object DepGraphTest extends Application { + + val global = new Global() + + var compilerchain = global.phaseDescriptors + + for(phase <- compilerchain) { + println(" - " + phase.phaseName) + } + +} + diff --git a/SIP/compiler-phase-init/sip-00002-11.png b/SIP/compiler-phase-init/sip-00002-11.png new file mode 100644 index 0000000000..c26f056d86 Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-11.png differ diff --git a/SIP/compiler-phase-init/sip-00002-12.png b/SIP/compiler-phase-init/sip-00002-12.png new file mode 100644 index 0000000000..b4e59a08f7 Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-12.png differ diff --git a/SIP/compiler-phase-init/sip-00002-13.png b/SIP/compiler-phase-init/sip-00002-13.png new file mode 100644 index 0000000000..9cfc543190 Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-13.png differ diff --git a/SIP/compiler-phase-init/sip-00002-14.png b/SIP/compiler-phase-init/sip-00002-14.png new file mode 100644 index 0000000000..d81df6dae3 Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-14.png differ diff --git a/SIP/compiler-phase-init/sip-00002-15.dia b/SIP/compiler-phase-init/sip-00002-15.dia new file mode 100644 index 0000000000..2b08cafc33 Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-15.dia differ diff --git a/SIP/compiler-phase-init/sip-00002-15.png b/SIP/compiler-phase-init/sip-00002-15.png new file mode 100644 index 0000000000..a9b0ce4db2 Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-15.png differ diff --git a/SIP/compiler-phase-init/sip-00002-16.dia b/SIP/compiler-phase-init/sip-00002-16.dia new file mode 100644 index 0000000000..e05924ac67 Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-16.dia differ diff --git a/SIP/compiler-phase-init/sip-00002-16.png b/SIP/compiler-phase-init/sip-00002-16.png new file mode 100644 index 0000000000..7351e6e7c9 Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-16.png differ diff --git a/SIP/compiler-phase-init/sip-00002-17.dia b/SIP/compiler-phase-init/sip-00002-17.dia new file mode 100644 index 0000000000..2e4068f82a Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-17.dia differ diff --git a/SIP/compiler-phase-init/sip-00002-17.png b/SIP/compiler-phase-init/sip-00002-17.png new file mode 100644 index 0000000000..e2026e51b4 Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-17.png differ diff --git a/SIP/compiler-phase-init/sip-00002-18.dia b/SIP/compiler-phase-init/sip-00002-18.dia new file mode 100644 index 0000000000..b1eb5003b4 Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-18.dia differ diff --git a/SIP/compiler-phase-init/sip-00002-18.png b/SIP/compiler-phase-init/sip-00002-18.png new file mode 100644 index 0000000000..1f743e549b Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-18.png differ diff --git a/SIP/compiler-phase-init/sip-00002-19.dia b/SIP/compiler-phase-init/sip-00002-19.dia new file mode 100644 index 0000000000..fc05ac6a2a Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-19.dia differ diff --git a/SIP/compiler-phase-init/sip-00002-19.png b/SIP/compiler-phase-init/sip-00002-19.png new file mode 100644 index 0000000000..ffd8930b59 Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-19.png differ diff --git a/SIP/compiler-phase-init/sip-00002-2.dia b/SIP/compiler-phase-init/sip-00002-2.dia new file mode 100644 index 0000000000..d08ee35f46 Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-2.dia differ diff --git a/SIP/compiler-phase-init/sip-00002-2.png b/SIP/compiler-phase-init/sip-00002-2.png new file mode 100644 index 0000000000..66a0eb695b Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-2.png differ diff --git a/SIP/compiler-phase-init/sip-00002-20.dia b/SIP/compiler-phase-init/sip-00002-20.dia new file mode 100644 index 0000000000..fb16133aa9 Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-20.dia differ diff --git a/SIP/compiler-phase-init/sip-00002-20.png b/SIP/compiler-phase-init/sip-00002-20.png new file mode 100644 index 0000000000..5290bb1d83 Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-20.png differ diff --git a/SIP/compiler-phase-init/sip-00002-21.dia b/SIP/compiler-phase-init/sip-00002-21.dia new file mode 100644 index 0000000000..65b16a99a0 Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-21.dia differ diff --git a/SIP/compiler-phase-init/sip-00002-21.png b/SIP/compiler-phase-init/sip-00002-21.png new file mode 100644 index 0000000000..152f0d7c0c Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-21.png differ diff --git a/SIP/compiler-phase-init/sip-00002-22.dia b/SIP/compiler-phase-init/sip-00002-22.dia new file mode 100644 index 0000000000..7ce832d3fa Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-22.dia differ diff --git a/SIP/compiler-phase-init/sip-00002-22.png b/SIP/compiler-phase-init/sip-00002-22.png new file mode 100644 index 0000000000..42cf265fcf Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-22.png differ diff --git a/SIP/compiler-phase-init/sip-00002-23.dia b/SIP/compiler-phase-init/sip-00002-23.dia new file mode 100644 index 0000000000..ae89016ca2 Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-23.dia differ diff --git a/SIP/compiler-phase-init/sip-00002-23.png b/SIP/compiler-phase-init/sip-00002-23.png new file mode 100644 index 0000000000..1b0ea408dc Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-23.png differ diff --git a/SIP/compiler-phase-init/sip-00002-24.dia b/SIP/compiler-phase-init/sip-00002-24.dia new file mode 100644 index 0000000000..d96f9cd319 Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-24.dia differ diff --git a/SIP/compiler-phase-init/sip-00002-24.png b/SIP/compiler-phase-init/sip-00002-24.png new file mode 100644 index 0000000000..cdb4c91223 Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-24.png differ diff --git a/SIP/compiler-phase-init/sip-00002-3.dia b/SIP/compiler-phase-init/sip-00002-3.dia new file mode 100644 index 0000000000..e8c883917e Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-3.dia differ diff --git a/SIP/compiler-phase-init/sip-00002-3.png b/SIP/compiler-phase-init/sip-00002-3.png new file mode 100644 index 0000000000..b464735965 Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-3.png differ diff --git a/SIP/compiler-phase-init/sip-00002-4.dia b/SIP/compiler-phase-init/sip-00002-4.dia new file mode 100644 index 0000000000..9927a1291b Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-4.dia differ diff --git a/SIP/compiler-phase-init/sip-00002-4.png b/SIP/compiler-phase-init/sip-00002-4.png new file mode 100644 index 0000000000..043520450b Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-4.png differ diff --git a/SIP/compiler-phase-init/sip-00002-5.dia b/SIP/compiler-phase-init/sip-00002-5.dia new file mode 100644 index 0000000000..b5bac4613f Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-5.dia differ diff --git a/SIP/compiler-phase-init/sip-00002-5.png b/SIP/compiler-phase-init/sip-00002-5.png new file mode 100644 index 0000000000..18a4b73dac Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-5.png differ diff --git a/SIP/compiler-phase-init/sip-00002-6.dia b/SIP/compiler-phase-init/sip-00002-6.dia new file mode 100644 index 0000000000..9e8f0a56bf Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-6.dia differ diff --git a/SIP/compiler-phase-init/sip-00002-6.png b/SIP/compiler-phase-init/sip-00002-6.png new file mode 100644 index 0000000000..8da5b03efe Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-6.png differ diff --git a/SIP/compiler-phase-init/sip-00002-7.dia b/SIP/compiler-phase-init/sip-00002-7.dia new file mode 100644 index 0000000000..9a35df0ee1 Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-7.dia differ diff --git a/SIP/compiler-phase-init/sip-00002-7.png b/SIP/compiler-phase-init/sip-00002-7.png new file mode 100644 index 0000000000..382a505c79 Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-7.png differ diff --git a/SIP/compiler-phase-init/sip-00002-8.dia b/SIP/compiler-phase-init/sip-00002-8.dia new file mode 100644 index 0000000000..6ccfa0f2d8 Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-8.dia differ diff --git a/SIP/compiler-phase-init/sip-00002-8.png b/SIP/compiler-phase-init/sip-00002-8.png new file mode 100644 index 0000000000..97a4c20fcb Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-8.png differ diff --git a/SIP/compiler-phase-init/sip-00002-9.dia b/SIP/compiler-phase-init/sip-00002-9.dia new file mode 100644 index 0000000000..da22427789 Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-9.dia differ diff --git a/SIP/compiler-phase-init/sip-00002-9.png b/SIP/compiler-phase-init/sip-00002-9.png new file mode 100644 index 0000000000..17109c13b6 Binary files /dev/null and b/SIP/compiler-phase-init/sip-00002-9.png differ diff --git a/SIP/compiler-phase-init/sip.xhtml b/SIP/compiler-phase-init/sip.xhtml new file mode 100644 index 0000000000..10cb4c3189 --- /dev/null +++ b/SIP/compiler-phase-init/sip.xhtml @@ -0,0 +1,1546 @@ + + + + + Scala Compiler Phase and Plug-In Initialization + + + + + + + + + + + + + + + + + + +

Scala Compiler Phase and Plug-In Initialization

+ +

Abstract

+ +

This document describes the design and implementation of a more + uniform way of handling internal compiler phases and external user + supplied phases via plug-ins in the Scala compiler.

+ +

Contents

+ + + + + +

Motivation

+ +

This section will cover the motivating goals of creating this + proposal and some use cases emphasizing these goals. There will also + be a section on goals that extend beyond this proposal and will + therefore not be covered by this proposal.

+ + +

Goals

+ + + +

Use Cases

+ +
    +
  1. Assembling compiler internal phases for + the JVM target and no plug-ins are loaded.
  2. + +
  3. Assembling compiler internal phases for + the MSIL target and no plug-ins are loaded.
  4. + +
  5. Adding one plug-in supplied phase to the + JVM target compiler after type checking.
  6. + +
  7. Adding two plug-in supplied phase to the + JVM target compiler after type checking.
  8. + +
  9. Adding two plug-in supplied phases, where + one has a dependency on the other, to the JVM target + compiler.
  10. + +
  11. Adding plug-in supplied phases with + dependencies to both plug-in supplied and internal phases to the + JVM target compiler.
  12. + +
  13. Two separate phases that should be handled + as a single phase.
  14. + +
  15. A cyclic dependency among phases is a + fatal error.
  16. + +
  17. Special handling of the + parser and terminal phases.
  18. + +
  19. Larger compiler example with three + plug-in supplied phases.
  20. + + +
+ + +

Detailed Use Cases

+ + +

[1] Assembling compiler internal phases for the JVM target and + no plug-ins are loaded.

+ +

All phases have to declare a runsAfter list of phase + names that should be run before the phase itself in the final + compiler phase chain. In this use case there are no plug-in supplied + phases, that have to be included. Depending on the command line + options and the JVM target the for this target default phases are + added to the phases set. This is a set, because a phase may only + occur once in the compiler phase chain.

+ +

Following these basic runsAfter constraints the + compiler phase chain is build from the phases set. In the example + below a simple set of phase constraints can be converted into a + graph, that in turn can be converted into a list of phases. +

+
+ +
+ +

Using the runsAfter list as the basic ordering + constraints there are some more properties that have to be + full filled. +

+ + + + +

[2] Assembling compiler internal phases for the MSIL target and + no plug-ins are loaded.

+ +

Like in use case 1 depending on command + line options and that the target being MSIL, the proper phases are + added to the phases set. Again there are no plug-in supplied phases + to be included. There are however some differences to use case 1. +

+ + + + + +

[3] Adding one plug-in supplied phase to the JVM target + compiler after type checking.

+ +

Like in use case 1 the internal phases + are added to the phases set. In this use case a plug-in is loaded + that will supply one phase to the phases set with a constraint that + it wants to run after the refchecks phase. The original + plug-in handling code will still handle loading plug-ins, disabling + plug-ins, but will no longer have the responsibility of assembling + the compiler chain. The plug-in is loaded and the phase is added + to the phases set together with the internal phases.

+

When assembling the compiler phase chain in this use case, there + will be two phases directly after the refchecks phase + and the dependencies can not be removed as described in use case 1. This situation is shown in the + example below.

+ +
+ +
+ +

One of the goals of this proposal is to be as compatible with the + current behavior of the compiler as possible. So if the situation is + as shown above that after including A, we can either take B or R, we + need a deterministic scheme for always constructing the same phase + chain. This is done by taking the plug-in supplied phases first and + then the internal phase after wards. +

+ + + + +

[4] Adding two plug-in supplied phase to the JVM target + compiler after type checking.

+ +

This use case is very similar to use case + 3, however instead of having one internal and one external phase + after the refchecks phase, we have one internal and two + external phases after the refchecks phase. This + situation is shown in the example below.

+ +
+ +
+ +

To be able to deterministically create the compiler phase chain + we need a rule to handle multiple external phases at the same + level. The simplest rule it to include the external phases + alphabetically order, as shown in the example above.

+ + + +

[5] Adding two plug-in supplied phases, where one has a + dependency on the other, to the JVM target compiler.

+ +

This use case is very similar to use case + 4, however instead of the two plug-in supplied phases having a + dependency on the same internal, one of the plug-in supplied phases + has a dependency on the other and the other has a dependency on an + internal phase. This is the example shown in the diagram below.

+ +
+ +
+ +

As described in use case 3, after + inclusion of phase A the next phase to include is phase T. In the + example above phase R is dependent on phase T, so the next to + include is phase R. This means that including phase T, means to + include T and the ordered sub tree, under phase T.

+ +

The example shown below, can be solved applying the same logic as + described above.

+ +
+ +
+ + + +

[6] Adding plug-in supplied phases with dependencies to both + plug-in supplied and internal phases to the JVM target + compiler.

+ +

This use case builds upon use case 1, use case 3, use case 4 + and use case 5 and shows how to handle + plug-in supplied phases with dependencies on other plug-in supplied + and internal phases.

+ +

In the example shown below the plug-in supplied phase T has + dependencies on the plug-in supplied phase P and the internal phase + B. The transformation shown below is made by applying the logic from + use case 1 to this example and using the + assumption that the dependencies of the internal phases will not + contain cycles and internal phases are visited last on each + level.

+ +
+ +
+ + +

[7] Two separate phases that should be handled as a single + phase.

+ +

In use case 1 we specified the assembly + of the compiler phase chain based on runs after + constraints. However, the compiler contains some phase pairs that + have very strong dependencies on each other or it simply does not + make sense to put a phase in between. In this use case we will focus + on the situation that we have two individual phase objects, but one + needs to run right after the other and thereby act as one large + phase.

+ +

The solution to this is to introduce a new runs right after + constraint to all phases. To keep the compatibility with current + plug-ins as high as possible, this runs right after constraint is + only mandatory for internal phases. In the example below the + runsRightAfter constraint is marked as a blue arrow and + behaves as a normal runs after constraint. +

+
+ +
+ +

In the example shown below, we can see the first four phases of + the Scala compiler. There is a runsRightAfter + constraint between typer and namer. There + is in this example a plug-in supplied phase (plugin1) that want to + run after the namer phase. The semantics of the run + after constraint is that is should run somewhere after a specified + phase, but as soon as possible. With this in mind it is perfectly + valid to change the dependency of plugin1 from + namer to typer.

+
+ +
+ +

If the phase plugin1 had declared to + runRightAfter the namer phase, this would + result in a unsolvable graph and would therefore generate an error, + because only one phase can come right after another phase. +

+ + +

[8] A cyclic dependency among phases is a fatal error.

+ +

Running the compiler without loading plug-ins, it is guaranteed + that there are do cyclic dependencies among the internal phases, so + the only way to introduce cycles in the phase graph is by loading + one or more plug-ins. If a cycle is detected in the phase graph the + compiler should refuse to start and give a proper error message.

+ +

In the example shown below a cycle is detected and marked in + pink. A cycle like this would generate a cyclic dependency error and + outputting the names of the involved phases.

+ +
+ +
+ + +

[9] Special handling of the parser and + terminal phases.

+ +

There are two very special phase objects in the phase graph. The + first phase is the parser phase. This phase will be the + head of the compiler phase chain, there are however no other + restrictions on this phase.

+ +

The other special phase is the terminal phase, which + has to be the last phase in the compiler phase chain. For this + reason it is not allowed to run after or run right after the + terminal phase. Any phase that specifies a runs after + or runs right after dependency on the terminal phase + will have that dependency dropped silently.

+ + + +

[10] Larger compiler example with three plug-in supplied phases.

+ +

This example is implemented in the reference implementation of the algorithm + below. In this example the compiler consists of nine internal phases + and two plug-ins are loaded adding a total of three phases to the + compiler. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Phase nameRuns afterRuns right after
Internal phases
nsc::parser
nsc::typernsc::parser
nsc::picklernsc::typer
nsc::liftcodensc::pickler
nsc::tailcallsnsc::pickler
nsc::liftcode
nsc::erasurensc::tailcalls
nsc::cleanupnsc::erasure
nsc::jvmnsc::cleanup
nsc::terminalnsc::jvm
nsc::msil
Plug-in supplied phases
plug1::optimization1nsc::typer
plug2::optimization1nsc::liftcode
plug2::optimization2plug2::optimization1
nsc::cleanup
+ +

Using the phases in the table above as the basis for the + dependency graph, the following graph will be constructed. This + graph contains all the information that is also present in the + phases set. As seen in the diagram below there are nodes without + phase objects (marked with dotted lines) and nodes with multiple + dependencies. There could also be cycles at this stage, but there + are non in this example. +

+ +
+ +
+ +

The transformation of the dependency graph into a dependency tree + is the basis of this proposal. The resulting dependency tree is + shown below. This tree is then the basis for generating the compiler + phase list. +

+ +
+ +
+ +

The resulting compiler phase list of traversing the tree above is + shown below. It is ensured that the parser phase is the + first and that the terminal phase is the last in this list.

+ + + + +

Future Goals That Extend Beyond This Proposal

+ + + + To the top + + +

Description

+ +

This section will cover a short description of the present design + and the full description of the proposed design changes, including + the constraint system and algorithm for solving the constraint + system. +

+ + +

The Present Design

+ +

Currently phases in the compiler are ordered statically by a hard + coded list structure in class Global (found in + Global.scala), where the method builtInPhaseDescriptors + returns the list of phase objects. The resulting list from invoking + this method is then extended with the phases from the plug-in + supplied by the user. This is done in the + computePhaseDescriptors method in class + Plugins (found in plugins/Plugins.scala). This method is + then again called from the method phaseDescriptors in + class Global. So all together the process of + calculating the phases of the compiler is spread over several files + and quite complicated.

+ +

A plug-in can supply a number of phases to the compiler and the + architecture of the plug-in subsystem already supply some of the + functionality that will be added to all internal phases. All phases + are a subclass of class SubComponent and all phases + supplied by plug-ins are subclasses of class + PluginComponent (which is a subclass of class + SubComponent).

+ + +

The Proposed Design

+ +

This section will cover the design of the proposed system for + handling inclusion of internal and external phases uniformly. It + will also cover a description of the constraint system and a + algorithm for solving these constraint for generating the compiler + phase chain. This design will focus on satisfying the use cases described earlier. +

+ +

From the use cases above it is possible to identify two kinds of + constraints. The first constraint is the runs after + dependency and the second constraint is the runs right after + dependency. It is necessary to declare that a given phase has to + runs after a list of phase names, however the runs right + after constraint only needs to declare the name one phase. +

+ + +

A goal of this work is to handle the inclusion and constraint + resolution of internal and plug-in supplied phases in a uniform way, + this also means that the responsibility of phase assembly should not + be handled by the plug-in subsystem any more, but by a separate + instance. For this separate instance we create a new + PhaseAssembly trait that is mixed into the + Global class in the same way as Plug-Ins + is mixed in. +

+ +

In the diagram below a schematic overview of the proposed system + is shown. A description of the individual components and the + modification needed are given below. +

+ +
+ +
+ + +

1. Internal Phases

+ +

All internal phase objects are declared in class + Global and will be extended to include the + runsAfter and the runsRightAfter variables + with the types described above. This will ensure that the internal + phases will always succeed in creating a compiler phase chain + depending on the compiler target. +

+

The phase objects that are needed for a given target will be + added to the phases set. See 3. Phases + Set for more information. +

+ + + +

2. External Phases

+ +

All external phases are supplied through plug-ins. All these + phases inherit from class PluginComponent. Defining the + runsRightAfter=None in class + PluginComponent will make the changes to the existing + plug-ins minimal, but makes it possible for external phases to use + the feature by simple overriding the variable if needed. +

+

It is still the responsibility of the plug-in subsystem to load + the actual plug-ins from the jar files and also the processing of + all the -Xplugin* compiler options available at the + command line. The phase assembly code currently present in the + plug-in subsystem will be replaced by code that adds all plug-in + supplied phases to the phases set. See 3. Phases Set for more information. +

+ + +

3. Phases Set

+ +

The phases set contains all the phase objects that should be + taken into consideration when performing compiler phase chain + assembly. This phases set would located in the class + Global. A proper Scala declaration of such a phases set + is shown below. +

+
protected val phasesSet : HashSet[SubComponent] = new HashSet[SubComponent]()
+ +

Ensuring that all phase objects are unique with respect to the + phase names, the hashCode method in class + SubComponent will be overridden to return the hash code + of the phase name and not the object itself. +

+ + +

4. Phase Assembly

+ +

From a compiler design perspective this component will include + the data structures and implementation of the constraint solving + algorithm that are needed to implement this proposal. It would be + integrated as a trait PhaseAssembly that is mixed into + class Global with this self type self: Global + =>. +

+ +

A very high level description of the actions performed by this + component can be split into three distinct parts. A detailed + algorithm that implements this high level description can be found + below in the section on the constraint + solving algorithm. +

+ +
    +
  1. Phases Set to Dependency Graph
    +

    From the phase objects in the phases set a dependency graph is + created, where the edges in this graph are constructed from the + runsAfter and runsRightAfter + constraints. Each node in the dependency graph has both a phase + name and a phase object, because all constraints in the phase + objects are only given by name. +

    +
  2. +
  3. Dependency Graph to Dependency Tree
    +

    To be able to create the compiler phase chain the dependency + graph has to be converted into a dependency tree. The dependency + graph contains nodes that have a phase name, but no phase + object. These nodes have to be removed. Also all edges in the + graph that are generated from runsRightAfter + constraints have to be handled special to enforce this constraint + and the graph is checked for cycles. If a cycle is found, the + algorithm will terminal with a fatal error, preventing the + compiler from starting. If no cycles are found in the dependency + graph is simplified with respect to what will become the root in + the dependency tree, by removing unneeded dependency edges. This + will transform the dependency graph into a dependency tree. +

    +
  4. +
  5. Dependency Tree to Compiler Phase List
    +

    The compiler phase list is constructed by traversing the + dependency tree from the root. The traversal algorithm will be + described below. +

    +
  6. +
+ + + +

5. Constraint System

+ +

The constraint system is implemented as a graph structure where + phases are modeled as nodes and the constraints are modeled as + directed edges. This section will describe the basic entities of the + constraint system and how they are modeled in the dependency graph.

+ + + + + + + + + + + + + + + +
+

Each node in the dependency graph contains several items of + information about the phase object and the dependencies to and + from it.

+
+
    +
  • phaseobj: (type: + SubComponent)
    A reference to the actual phase + object, if there is any. If there is a phase object then + there is also a phase name, but the constraints are only + given by name, so some nodes may have a phase name and no + phase object. +
  • +
  • phasename: (type: String)
    + The name of the phase object. This is always set. +
  • +
  • after: (type: HashSet[Edge]()) +
    + This is the set of Edge object that point to + Node objects that have to come before this + Node. So all runsAfter and + runsRightAfter constraints given by a phase + object are present in this after list. +
  • +
  • deps: (type: HashSet[Edge]()) +
    + This is the set of Edge object that points to + this Node object. So its the opposite of the + after list. +
  • +
+
+
+ +
+
+

Each edge in the dependency graph is directed, so it has + references to its to and from Node objects and + knows if it is created from a runsAfter + constraint (soft) or created from a + runsRightAfter constraint (hard).

+
+
    +
  • to: (type: Node) +
    + This is a reference to the Node object this + Edge object is pointing to. This means that the + frm node has a dependency on the + to node. +
  • +
  • frm: (type: Node) +
    + This is a reference to the Node object this + Edge object is pointing from. +
  • +
  • hard: (type: Boolean) +
    + In the dependency graph we have soft and hard + dependencies. The soft dependencies are created from the + runsAfter list of constraints. The hard + dependencies are created from the + runsRightAfter constraint, if any. +
  • +
+
+
+ +
+
+ +

Below is the interface implementation of the dependency graph in + Scala. There are classes implementing the edges and nodes and the + container for storing the edges and nodes and aux. method for easy + access to the stored information. +

+
+class Edge(f: Node, t: Node, h: Boolean) {
+  var frm = f
+  var to = t
+  var hard = h
+}
+
+class Node(phs:SubComponent, name:String) {
+  var phasename: String = name
+  var phaseobj: SubComponent = phs
+  var after: HashSet[Edge] = new HashSet[Edge]()
+  var deps: HashSet[Edge] = new HashSet[Edge]()
+}
+
+class DependencyGraph {
+  
+  val nodes = new HashMap[String,Node]()
+  val edges = new HashSet[Edge]()
+
+  def getNodeByPhase(name : String) : Node
+  def getNodeByPhase(phs : SubComponent) : Node
+  def softConnectNodes(frm: Node, to: Node) : Unit
+  def hardConnectNodes(frm: Node, to: Node) : Unit
+}
+  
+ + + +

Constraint Solving Algorithm

+ +

This algorithm will build a list of phase objects from a set of + phase objects with constraints. The position of each phase object in + the final list will ensure that all its constraints are + satisfied. The algorithm does this by constructing a dependency + graph from the set of phase objects. This dependency graph is then + over a series of steps converted into a dependency tree with a root + node. To construct the final compiler phase list the dependency tree + is traversed from the root building the list of phase objects.

+ +

The function below is the top most function of this algorithm + that transforms the set of phase objects into a ordered list of + phase objects so all constraints are satisfied. This function + follows the description given about the phase + assembly component. All the auxiliary functions will be covered + below.

+ +
  function phasesSetToPhasesList(phasesSet)
+
+      // call function to generate dependency graph from phases set    
+      graph <- phasesSetToDepGraph( phaseSet )
+
+      // call function to transform the dependency graph to a dependency tree.
+      // if the dependency graph contains a cycle a fatal error will be generated.
+      rootnode <- depGraphToDepTree( graph )
+
+      // call function to recursively build phase list from the root of 
+      // the dependency tree
+      phaselist <- depTreeToPhaseList( rootnode, new List() )
+
+      return phaselist
+
+ +

List of functions called:

+ + + +
The phasesSetToDepGraph function
+ +

The dependency graph, with the elements described in the section about the constraint system, is + build from the phase objects in the phases set.
+ It works as follows: +

+ + +

A pseudo code version of this algorithm is shown below.

+ +
  function phasesSetToDepGraph( phaseSet )
+      // create new graph structure to hold data    
+      graph <- new DependencyGraph
+      // iterate over all phase objects in the phasesSet
+      for phs in phasesSet
+          // create a node from the phase object
+          fromnode <- graph.getNodeByName(phs)
+	  // test if the phase object has a runs right after constraint
+	  if phs.runRightAfter not eq ""
+	      // create a node from the phase constraint name and connect with hard edge
+	      tonode <- graph.getNodeByName( phs.runRightAfter )
+	      graph.hardConnectNodes( fromnode, tonode )
+	  else
+              // the phase object did not have a runs right after constraint
+              // iterate over all phase constraints in the runs after constraint list
+	      for phsname in phs.runsAfter
+                  // create a node from the phase constraint name and connect with soft edge
+	          tonode <- graph.getNodeByName( phsname )
+		  graph.softConnectNodes( fromnode, tonode )
+
+      return graph
+
+ + + + +
The depGraphToDepTree function
+ +

The process of converting the dependency graph into a dependency + tree is complex, so to simplify the algorithm the individual steps + are split into functions.
+ It works as follows: +

+ + + +

Below is shown a pseudo code version of the function.

+ +
  function depGraphToDepTree( graph ) 
+    
+      // Remove all nodes where phaseobj == null
+      removeDanglingNodes( graph ) 
+
+      // Enforce hardlinks / runsRightAfter and promote nodes down the tree
+      enforceHardlinks( graph )
+
+      // Extract the root node from the graph
+      root <- graph.getNodeByPhase( ROOTNAME )
+
+      // Test for cycles in the graph, if found generate fatal error
+      testForCycles( root, new Set() )
+
+      // Simplify graph by removing edges, starting from the given root node
+      simplifyGraphFromNode( root, graph )
+
+      return root
+  
+ +

List of functions called:

+ + + +
The depTreeToPhaseList function
+ +

The graph has been transformed into a tree and a reference to the + root node is identified. The tree is now traversed from the root + node, by calling itself recursively on each node object. The empty + list is given to the function together with the root node and the + phase objects are added to the list as the function traverses the + tree.
+ It works as follows:

+ + + +

Below is shown a pseudo code version of the function.

+ +
  function depTreeToPhaseList( node, chain )
+      // add the phase object of the node to the chain list
+      chain <- chain.append( node.phaseobj )
+      // no more depends, return the chain
+      if node.deps.length == 0
+          return chain
+      // only one depend, so no need to order one element, just call recursively 
+      else if node.deps.length == 1
+          return depTreeToPhaseList( node.deps.getFirst().frm,  chain )
+      // more then one depend
+      else
+          // the depends are ordered and saved as a list
+          order <- dependencyOrder( node.deps )
+          // iterate over the list of ordered depend nodes and call recursively 
+          for nd in order
+              chain <- depTreeToPhaseList( nd, chain )
+          // return the complete chain
+          return chain
+  
+ +

List of functions called:

+ + + +
Helper function removeDanglingNodes
+ +

This helper function will given a dependency graph find all nodes + that have no reference to a phase object and remove these nodes from + the graph. (See use caes 2) These nodes that + have no phase object are generated from runs after or runs right + after constraints to phases that are not loaded.
It is done as + follows:

+ + + +

Below is shown a pseudo code version of the function.

+ +
  function removeDanglingNodes( graph )
+      // find all nodes with no phase object
+      dnodes <- filter( lambda node: node.phaseobj == null, graph.nodes )
+      // iterate over these nodes and remove them one by one
+      for node in dnodes
+          graph.nodes.remove( node )
+          // also remove all edge objects related to this node object
+          for edge in node.deps
+              graph.edges.remove( edge )
+              edge.frm.after.remove( edge )
+  
+ + + +
Helper function enforceHardlinks
+ +

This helper function will given a dependency graph find all edges + in the graph that are marked as hard. It will then ensure that the + to node the edge is pointing to only has this edge in + its deps list. (See use case 7).
+ It works as follows: +

+ + + +

Below is shown a pseudo code version of the function.

+ +
  function enforceHardlinks( graph )
+      do
+          // lets assume that no edges are moved until proven otherwise
+          rerun <- false
+          // find all edges in the graph that are marked as hard
+          hardlinks <- filter( lambda e: e.hard, graph.edges )
+          // iterate over all hard edges found
+          for edge in hardlinks
+              // find all hard edges in the deps list of the node this edge points to
+              sanity <- filter( lambda e: e.hard, edge.to.deps )
+     	      if sanity.length > 1
+     	      // generate fatal error, there is more then one runs right 
+              // after constraint on the same node
+     	      else
+                  // find all non hard edges in the deps list of the node this edge points to
+     	          promote <- filter( lambda e: not e.hard, edge.to.deps )
+                  // assign the list of hard edges to the deps list of the node this 
+                  // edge points to
+     	          edge.to.deps <- sanity
+                  // iterate over all non hard edges and attach them to the deps list of the 
+                  // node this edge points from
+     	          for pedge in promote
+		      // we have moved an edge - we need to rerun this to check
+                      rerun <- true
+                      // move the edge object down along the hard edge
+                      pedge.to <- edge.frm
+                      edge.frm.deps.attach(pedge)
+      // if edges where moved, then rerun to make sure the structure is valid
+      while rerun
+  
+ +

To better understand this little function, lets look at some + diagrams and how this function works on these examples. The first + example is shown below. Here phase B wants to run right after phase + A and phase C just wants to run somewhere after phase A, so there is + no problem in pushing phase C down to run after phase B. The + function does this be first finding all the hard edges (the blue + edges). It then inspects the edges one by one and in this example we + have only one hard edge. The edge goes from phase B to phase A, + meaning that phase B want to run right after phase A. The algorithm + now looks at the deps list of phase A and finds that + there is only one hard edges (this is very good). The algorithm now + filters out the non-hard edges and finds the edge to phase C. This + edge is now detached from phase A and attached to phase B. +

+ +
+ +
+ +

With the same arguments can we also handle the situation shown + below.

+ +
+ +
+ + + +
Helper function testForCycles
+ +

This helper function will, given a node in the graph and a set + containing node names, determine of there are any cycles in the + graph. This will ensure that use case 8 is + addressed. Please note that this function will call itself + recursively and is called the first time with the root node and an + empty set of node names. +
+ It is done as follows: +

+ + + +

Below is shown a pseudo code version of the function.

+ +
  function testForCycles( node, names )
+      // test if the name of the node is already in the set
+      if names.contains( node.phasename )
+          // names are unique, so this is a fatal error, there is a cycle
+          // the same name has been visited twice
+
+      // add the name of the node to the set
+      names.add( node.phasename )
+      // use the dependencyOrder function to sort the deps of the node
+      nodes <- dependencyOrder( node.deps )
+      // iterate over all deps nodes and call recursively
+      for nd in nodes
+          testForCycles(nd, names)
+      
+      // remove the name of the node from the set
+      names.remove( node.phasename )
+  
+ +

List of functions called:

+ + +

There are other ways of detecting cycles in graphs, that are + faster and more efficient, please read + here for more information.

+ + +
Helper function simplifyGraphFromNode
+ +

This helper function will, given a node in the graph and a + reference to the graph structure, remove unneeded edges and + transforming the graph into a tree. Please note that this + function will call itself recursively and is called the first time + with the root node and the full graph. +
+ It is done as follows: +

+ + + +

Below is shown a pseudo code version of the function.

+ +
  function simplifyGraphFromNode( node, graph )
+      removal <- new List()
+      for edge in node.deps
+          if edge.frm.after.length > 1
+              removal.append( edge )
+    
+      for edge in removal
+          node.deps.remove( edge )
+	  edge.frm.after.remove( edge )
+	  graph.edges.remove( edge )
+
+      nodes <- dependencyOrder( node.deps )
+      for nd in nodes
+          simplifyGraphFromNode( nd, graph )
+  
+ +

List of functions called:

+ + + +

In the example shown below we simplify this graph into a tree + from the node A. This means that we start the traversal from node A + and uses the A.deps list to continue the traversal. If + A.deps contains more then one element, the list is + sorted so we first visit all plug-in supplied phases in alphabetical + order and then the internal phase. In this example this gives the + order [P,R,B]. Before the algorithm traverses to a new + node it checks that node for its number of after + links. If it has more then one after link, it simply deletes the + link to it so it can carry on. In the example below this is the case + of node P and T. When the algorithm visits node P and wants to + traverse on to node T, it checks the number of after + constraints in node T. Node T has two after constraints so it can + safely delete the edge between node P and T, because there must be + another node that we have not included yet, that will include this + node T.

+ +
+ +
+ + + +
Helper function dependencyOrder
+ +

This helper function will given a list of edges sort them + according to a specific scheme are return them as a list. The + sorting scheme is as follows. The returned list is the reverse of + first having the plug-in supplied phases in alphabetical order + followed by the internal phases.
+ This is done as follows: +

+ + +

Below is shown a pseudo code version of the function.

+ +
  function dependencyOrder( deps )
+      external <- filter(lambda e: (! e.frm.phaseobj.internal), deps)
+      internal <- filter(lambda e: e.frm.phaseobj.internal, deps)
+
+      extnodes <- map(lambda e: e.frm, external)
+      extnodes <- extnodes.sort(lambda (n1,n2): (n1.phasename compareTo n2.phasename) < 0)
+      extnodes <- extnodes.reverse
+	
+      nodes <- (map(lambda e: e.frm, internal)).extend( extnodes )
+      nodes <- nodes.reverse
+      return nodes
+  
+ + + + +

Naming Scheme for Internal and External Phases

+ +

Having the ability to load a large number of plug-ins, where each + plug-in can supply several named phases, gives a high possibility of + name clashes. There is no way to enforce unique names in plug-in + supplied phases because they can have inter dependencies. The best + solution is to suggest a naming scheme for phase names.

+ +

This naming scheme composes all names from the package/plug-in + name, then a double colon :: and then the actual phase + name. So for the internal compiler phase names this would be:

+ +
    val phaseName = "nsc::tailcalls"
+ +

For a plug-in with name unit that supplies a phase + with name convert the full phase name would be:

+ +
    val phaseName = "unit::convert"
+ +

By using this naming scheme for all phases there is a namespace + for phase names within each plug-in and external phases will never + clash with internal phase names.

+ + +

Implications of This Proposal

+ +

All plug-ins need to be updated to the design in this + proposal. This means that all current plug-ins will not be able to + load. However the modifications to the existing plug-ins are + minor. Current variables and types: +

+
  val runsAfter: String
+

The proposed changes are:

+
  val runsAfter: List[String]
+ +

All internal phases in the compiler need to declare there + ordering. This means that instead of having the ordering very + explicit in the list structure, the ordering is encoded into each + phase object. The following signatures will be added to the + SubComponent class.

+ +
  val runsAfter: List[String]
+  val runsRightAfter: Option[String]
+ +

The following items must be added to each phase + object to implement the signatures (however they must be adapted to + the individual phase object).

+ +
  val runsAfter = List[String]("phasename")
+  val runsRightAfter = None
+ +

There is no reason to change the names of the phases as suggested in + the section above, but it will minimize the chance of name clashes + and will also minimize the chance of accidental dependencies.

+ + To the top + + +

Implementation

+ +

An example implementation has been created that follows the + algorithm sketched in this proposal and models the compilers + internal structure. This implementation can be found in the file sip-00002-10.scala. The example + implementation uses the phases from use case + 10 as its data. +

+ +

Running the example code

+ + + + +

Console output

+ +
$ scalac sip-00002-10.scala 
+$ scala depgraph.DepGraphTest
+[dropping depend on node with no phase: nsc::msil]
+[removing edge: nsc::tailcalls => nsc::pickler]
+[removing edge: plug2::optimization2 => plug2::optimization1]
+ - nsc::parser
+ - nsc::typer
+ - plug1::optimization1
+ - nsc::pickler
+ - nsc::liftcode
+ - plug2::optimization1
+ - nsc::tailcalls
+ - nsc::erasure
+ - nsc::cleanup
+ - plug2::optimization2
+ - nsc::jvm
+ - nsc::terminal
+$ 
+ +

PNG images of the generated dot files showing the graph + structure. Internal phases are marked as black circles and plug-in + phases are marked as green circles. The hard links (runsRightAfter + constraints) are marked as blue arrows. Click on the pictures to + enlarge.

+ + + + + + + + + +
1.
+
+ + + +
+
2.
+
+ + + +
+
3.
+
+ + + +
+
4.
+
+ + + +
+
+ + To the top + + + diff --git a/SIP/presip-classic.xhtml b/SIP/presip-classic.xhtml new file mode 100644 index 0000000000..3dac7c6e1d --- /dev/null +++ b/SIP/presip-classic.xhtml @@ -0,0 +1,29 @@ + + + + + The Scala Classic Calculus + + + + + + + + + + + +

The Scala Classic Calculus

+ +

Copyright © 2008, Geoffrey Washburn

+ +

Abstract

+ + This document describes the Scala Classic calculus that provides a + verified formal modal for a subset of the Scala language. This + calculus can be useful in understanding the Scala language + independently of the Scala Language Specification. + + + diff --git a/SIP/presip-constraints.xhtml b/SIP/presip-constraints.xhtml new file mode 100644 index 0000000000..a53d32e97a --- /dev/null +++ b/SIP/presip-constraints.xhtml @@ -0,0 +1,29 @@ + + + + + Generalized type constraints + + + + + + + + + + + + + + + + + +

Generalized type constraints

+

Copyright © 2008, Geoffrey Washburn

+ +

Abstract

+ + + diff --git a/SIP/presip-depmethods.xhtml b/SIP/presip-depmethods.xhtml new file mode 100644 index 0000000000..170a018423 --- /dev/null +++ b/SIP/presip-depmethods.xhtml @@ -0,0 +1,29 @@ + + + + + Dependent method types + + + + + + + + + + + + + + + + + +

Dependent method types

+

Copyright © 2008, Geoffrey Washburn

+ +

Abstract

+ + + diff --git a/SIP/sip.css b/SIP/sip.css new file mode 100644 index 0000000000..6c1a877fc9 --- /dev/null +++ b/SIP/sip.css @@ -0,0 +1,5 @@ +/* Default stylesheet for SIPs */ + +body { + width: 40em; +} \ No newline at end of file diff --git a/SIP/virtual-traits/sip-0000X-1.xhtml b/SIP/virtual-traits/sip-0000X-1.xhtml new file mode 100644 index 0000000000..e9cda55ae6 --- /dev/null +++ b/SIP/virtual-traits/sip-0000X-1.xhtml @@ -0,0 +1,100 @@ + + + + + Virtual Traits for Scala : Bibtex + + + + +
+@book{madsen93:_objec_orien_progr_beta_progr_languag,
+  author = {Madsen, Ole Lehrmann and Nygaard, Kristen and M\o{}ller-Pedersen, Birger},
+  title = {Object-Oriented Programming in The Beta Programming Language},
+  publisher = {Addison-Wesley},
+  year = 1993
+}
+
+ +
+@inproceedings{Mads89a,
+  author = {Ole Lehrmann Madsen and Birger M{\o}ller-Pedersen},
+  title = {Virtual Classes: {A} Powerful Mechanism
+                  in Object-Oriented Programming},
+  booktitle = {Proceedings {OOPSLA}'89, {ACM SIGPLAN Notices}},
+  pages = {397--406},
+  volume = {24, 10},
+  month = oct,
+  year = {1989}
+}
+
+ +
+@phdthesis{ernst99b,
+  author = {Erik Ernst},
+  title = {gbeta  -- a Language with Virtual Attributes, Block Structure, and Propagating, Dynamic Inheritance},
+  school = {Department of Computer Science, University of Aarhus, \AA{}rhus, Denmark},
+  year = {1999}
+}
+
+ +
+@inproceedings{1111062,
+  author = {Erik Ernst and Klaus Ostermann and William R. Cook},
+  title = {A virtual class calculus},
+  booktitle = {POPL '06: Conference record of the 33rd ACM SIGPLAN-SIGACT symposium on Principles of programming languages},
+  year = 2006,
+  isbn = {1-59593-027-2},
+  pages = {270--282},
+  location = {Charleston, South Carolina, USA},
+  doi = {http://doi.acm.org/10.1145/1111037.1111062},
+  publisher = {ACM Press},
+  address = {New York, NY, USA}
+}
+
+ +
+@inproceedings{Erns01a,
+  author = {Erik Ernst},
+  booktitle = {ECOOP 2001},
+  editor = {J. L. Knudsen},
+  number = {2072},
+  pages = {303--326},
+  publisher = {Springer Verlag},
+  series = {LNCS},
+  title = {Family Polymorphism},
+  year = {2001}
+}
+
+ +
+@inproceedings{EE99,
+  author = {Erik Ernst},
+  title = {Propagating Class and Method Combination},
+  pages = {67--91},
+  editor = {Rachid Guerraoui},
+  booktitle = {Proceedings {ECOOP}'99},
+  address = {Lisboa, Portugal},
+  month = jun,
+  year = 1999,
+  series = {LNCS 1628},
+  publisher = {Springer-Verlag},
+  isbn = {ISBN 3-540-66156-5}
+}
+
+ +
+@inproceedings{aracic06:_overv_of_caesarj,
+  author = {Ivica Aracic and Vaidas Gasiunas and Mira Mezini and Klaus Ostermann},
+  title = { An Overview of CaesarJ},
+  booktitle = {Transactions on Aspect-Oriented Software Development I},
+  pages = {135-173},
+  year = 2006,
+  volume = {3880/2006},
+  series = {Lecture Notes in Computer Science},
+  publisher = {Springer Berlin / Heidelberg}
+}
+
+ + + diff --git a/SIP/virtual-traits/sip-0000X.xhtml b/SIP/virtual-traits/sip-0000X.xhtml new file mode 100644 index 0000000000..170a1ec436 --- /dev/null +++ b/SIP/virtual-traits/sip-0000X.xhtml @@ -0,0 +1,341 @@ + + + + + Virtual Traits for Scala + + + + + + + + + + + + + + + + + + +

Virtual Traits for Scala

+ +

Abstract

+ +

This document describes the design, integration and implementation + of Virtual Traits (VT) into the Scala language. Virtual traits in + Scala are modeled after the BETA [1] and + gbeta [3] notion of virtual classes [2, 4].

+ +

Contents

+ + + + + +

Motivation

+ +

The Scala language already has abstract type members that behave + like the types of virtual classes, however this is only for + types. Scala supports both classes and traits, where traits act as + mixins that can be composed together to create classes. The reason + why there are both classes and traits, is to support the + interoperability with Java which has only classes and for this + reason classes are treated specially in the compiler.

+ +

Using abstract type members and a complicated rewriting scheme it + is possible to create virtual classes manually, but there are + several typing issues which will be covered later. We will in this + proposal show the extensions to the Scala language specification to + include virtual traits, the rewriting scheme for translating virtual + trait programs into regular Scala programs and discuss the + implementation of this in the Scala compiler.

+ +

This proposal builds upon the parametrized traits.

+ + +

Goals

+ + + + + +

Use cases

+ +
    +
  1. Initial binding of virtual trait
  2. +
  3. Further binding of a virtual trait
  4. +
  5. Final binding of a virtual trait
  6. +
  7. Diamond inheritance involving virtual traits
  8. +
  9. Virtual traits with nested virtual traits
  10. +
+ + +

Use cases in detail

+ + +

(1) Initial binding of virtual trait

+ +

+ +
+    trait A <: { ... }
+    trait B <: A { ... }
+    trait C <: Foo with B { ... }
+  
+ + +

(2) Further binding of a virtual trait

+ +

+ +
+    override trait A <: { ... }
+    override trait B <: { ... }
+    override trait C <: Bar { ... }
+  
+ + +

(3) Final binding of a virtual trait

+ +

+ +
+    override trait A
+    override trait B { ... }
+    override trait C extends Baz { ... }
+  
+ + +

(4) Diamond inheritance involving virtual traits

+ +

+ +
+ trait A {
+   trait V <: { ... }
+ }
+ trait B extends A {
+   override trait V <: { ... }
+ }
+ trait C extends A {
+   override trait V <: { ... }
+ }
+ trait D extends B with C {
+   override trait V <: { ... }
+ }  
+  
+ + +

(5) Virtual traits with nested virtual traits

+ +

+ +
+ trait A {
+   trait V <: {
+     trait W <: { ... }
+   }
+ }
+ trait B extends A {
+   override trait V <: {
+     override trait W <: { ... }
+   }
+ }
+  
+ + +

Future goals that extend beyond this proposal

+ + + + +

Language specification changes

+ + This section will cover the changes to the Scala language, both the + syntactic and sematic changes that are nessesary to make virtual + traits work under Scala. + +

+ Normal declaration of a trait in Scala. +

+
+    trait Foo { ... }
+  
+ +
+    TmplDef ::= ['case'] 'class' ClassDef
+          | ['case'] 'object' ObjectDef
+          | ['override'] 'trait' TraitDef
+
+TraitDef ::= id [TypeParamClause] TraitTemplateOpt
+
+TraitTemplateOpt ::= TraitExtends TraitTemplate
+                   | [['extends'] TemplateBody ]
+                   | '<:' TemplateBody
+
+TraitExtends ::= 'extends' | '<:'
+
+
+---------- FLAGS -------------
+
+TRAIT + DEFERED + ! OVERRIDE => Initial binding
+TRAIT + DEFERED + OVERRIDE   => Further binding
+TRAIT + ! DEFERED + OVERRIDE => Final binding
+
+classes that extend virtuals are marked with a VIRTUALSUBCLASS
+
+  
+ +

Syntactic

+ + From the virtual class litterateur there is a distinction between + the initial binding and the further binding of a virtual class. To + have the same expressiveness in Scala this is the way to declare a + initial binding of a virtual trait. +
+  trait Bar <: { ... }
+  
+ To be able to tell the difference between a initial and a further + binding of a trait on the syntactical level, further bindings are + written as. +
+  override trait Bar <: { ... }
+  
+ +

Semantic

+ + + +

Implementation

+ + + + +

References

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ [1] + + Ole Lehrmann Madsen, Kristen Nygaard, and Birger Møller-Pedersen. + Object-Oriented Programming in The Beta Programming Language. + Addison-Wesley, 1993. + [ bib ] +
+ [2] + + Ole Lehrmann Madsen and Birger Møller-Pedersen. + Virtual classes: A powerful mechanism in object-oriented + programming. + In Proceedings OOPSLA'89, ACM SIGPLAN Notices, volume 24, + 10, pages 397-406, October 1989. + [ bib ] +
+ [3] + + Erik Ernst. + gbeta - a Language with Virtual Attributes, Block Structure, + and Propagating, Dynamic Inheritance. + PhD thesis, Department of Computer Science, University of Aarhus, + Århus, Denmark, 1999. + [ bib ] +
+ [4] + + Erik Ernst, Klaus Ostermann, and William R. Cook. + A virtual class calculus. + In POPL '06: Conference record of the 33rd ACM SIGPLAN-SIGACT + symposium on Principles of programming languages, pages 270-282, New York, + NY, USA, 2006. ACM Press. + [ bib ] +
+ [5] + + Erik Ernst. + Family polymorphism. + In J. L. Knudsen, editor, ECOOP 2001, number 2072 in LNCS, + pages 303-326. Springer Verlag, 2001. + [ bib ] +
+ [6] + + Erik Ernst. + Propagating class and method combination. + In Rachid Guerraoui, editor, Proceedings ECOOP'99, LNCS 1628, + pages 67-91, Lisboa, Portugal, June 1999. Springer-Verlag. + [ bib ] +
+ [7] + + Ivica Aracic, Vaidas Gasiunas, Mira Mezini, and Klaus Ostermann. + An overview of caesarj. + In Transactions on Aspect-Oriented Software Development I, + volume 3880/2006 of Lecture Notes in Computer Science, pages 135-173. + Springer Berlin / Heidelberg, 2006. + [ bib ] +
+ + + diff --git a/build.number b/build.number index b96c8a8f69..88a6ad113e 100644 --- a/build.number +++ b/build.number @@ -1,5 +1,5 @@ #Tue Sep 11 19:21:09 CEST 2007 -version.minor=7 -version.patch=2 -version.suffix=RC4 +version.minor=8 +version.patch=0 +version.suffix=alpha version.major=2 diff --git a/lib/scala-compiler.jar.desired.sha1 b/lib/scala-compiler.jar.desired.sha1 index a2f0fac1e6..b3a59785b2 100644 --- a/lib/scala-compiler.jar.desired.sha1 +++ b/lib/scala-compiler.jar.desired.sha1 @@ -1 +1 @@ -467e21e8f22b32953dbe41a9a091f9524816b61b ?scala-compiler.jar +7cd5469aa4e85530aa99234d983952589a030e8e ?scala-compiler.jar diff --git a/lib/scala-library-src.jar.desired.sha1 b/lib/scala-library-src.jar.desired.sha1 index b2103d3ed8..53f7fee70a 100644 --- a/lib/scala-library-src.jar.desired.sha1 +++ b/lib/scala-library-src.jar.desired.sha1 @@ -1 +1 @@ -feb5f36c8fc1eefd7c01a17cf7c1d7497e4adc4e ?scala-library-src.jar +6f717f2a07d59605f1937cae43bfb0c4e7376937 ?scala-library-src.jar diff --git a/lib/scala-library.jar.desired.sha1 b/lib/scala-library.jar.desired.sha1 index 83fb1fd94d..7adc0e712b 100644 --- a/lib/scala-library.jar.desired.sha1 +++ b/lib/scala-library.jar.desired.sha1 @@ -1 +1 @@ -79ec54337732231ba14fb716ee3feea79ec34a8e ?scala-library.jar +4ad240bdf0649fb2b07b3d18a555134651329910 ?scala-library.jar diff --git a/src/compiler/scala/tools/nsc/Settings.scala b/src/compiler/scala/tools/nsc/Settings.scala index add169ad53..7a6452709e 100644 --- a/src/compiler/scala/tools/nsc/Settings.scala +++ b/src/compiler/scala/tools/nsc/Settings.scala @@ -155,6 +155,7 @@ class Settings(error: String => Unit) { val XnoVarargsConversion = BooleanSetting("-Xno-varargs-conversion", "disable varags conversion") val selfInAnnots = BooleanSetting ("-Yself-in-annots", "Include a \"self\" identifier inside of annotations") + val suppressVTWarn = BooleanSetting ("-Ysuppress-vt-typer-warnings", "Suppress warnings from the typer when testing the virtual class encoding, NOT FOR FINAL!") /** A list of all settings */ def allSettings: List[Setting] = allsettings.reverse diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 94f6039737..b0b55c5528 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -1,5 +1,5 @@ /* NSC -- new Scala compiler - * Copyright 2005-2007 LAMP/EPFL + * Copyright 2005-2008 LAMP/EPFL * @author Martin Odersky */ // $Id$ @@ -150,7 +150,7 @@ trait Parsers extends NewScanners with MarkupParsers { t } -/////////// PLACEHOLDERS /////////////////////////////////////////////////////// +/* -------- PLACEHOLDERS ------------------------------------------- */ /** The implicit parameters introduced by `_' in the current expression. * Parameters appear in reverse order @@ -200,7 +200,7 @@ trait Parsers extends NewScanners with MarkupParsers { t } -/////// ERROR HANDLING ////////////////////////////////////////////////////// +/* -------- ERROR HANDLING ------------------------------------------- */ protected def skip() { var nparens = 0 @@ -326,7 +326,7 @@ trait Parsers extends NewScanners with MarkupParsers { def errorTermTree = Literal(Constant(null)).setPos((inCurrentPos)) def errorPatternTree = Ident(nme.WILDCARD).setPos((inCurrentPos)) -/////// TOKEN CLASSES ////////////////////////////////////////////////////// +/* -------- TOKEN CLASSES ------------------------------------------- */ def isModifier: Boolean = inToken match { case ABSTRACT | FINAL | SEALED | PRIVATE | PROTECTED | OVERRIDE | IMPLICIT | LAZY => true @@ -374,7 +374,7 @@ trait Parsers extends NewScanners with MarkupParsers { def isStatSep: Boolean = isStatSep(inToken) -/////// COMMENT AND ATTRIBUTE COLLECTION ////////////////////////////////////// +/* -------- COMMENT AND ATTRIBUTE COLLECTION ------------------------------------------- */ /** Join the comment associated with a definition */ @@ -384,7 +384,7 @@ trait Parsers extends NewScanners with MarkupParsers { else trees } -/////// TREE CONSTRUCTION //////////////////////////////////////////////////// +/* -------- TREE CONSTRUCTION ------------------------------------------- */ /** Convert tree to formal parameter list */ @@ -448,7 +448,7 @@ trait Parsers extends NewScanners with MarkupParsers { Function(List(makeSyntheticParam(pname)), insertParam(tree)) } -/////// OPERAND/OPERATOR STACK ///////////////////////////////////////////////// +/* -------- OPERAND/OPERATOR STACK ------------------------------------------- */ var opstack: List[OpInfo] = Nil @@ -499,7 +499,7 @@ trait Parsers extends NewScanners with MarkupParsers { top } -/////// IDENTIFIERS AND LITERALS //////////////////////////////////////////////////////////// +/* -------- IDENTIFIERS AND LITERALS ------------------------------------------- */ def ident(): Name = @@ -659,7 +659,7 @@ trait Parsers extends NewScanners with MarkupParsers { if (inToken == NEWLINE && p(inNextTokenCode)) newLineOpt() } -//////// TYPES /////////////////////////////////////////////////////////////// +/* -------- TYPES ------------------------------------------- */ /** TypedOpt ::= [`:' Type] */ @@ -900,7 +900,7 @@ trait Parsers extends NewScanners with MarkupParsers { typ() } -//////// EXPRESSIONS //////////////////////////////////////////////////////// +/* -------- EXPRESSIONS ------------------------------------------- */ /** EqualsExpr ::= `=' Expr */ @@ -1317,7 +1317,7 @@ trait Parsers extends NewScanners with MarkupParsers { } //def p2i(pos : ScanPosition) : Int; -//////// PATTERNS //////////////////////////////////////////////////////////// +/* -------- PATTERNS ------------------------------------------- */ /** Patterns ::= Pattern { `,' Pattern } * SeqPatterns ::= SeqPattern { `,' SeqPattern } @@ -1493,7 +1493,7 @@ trait Parsers extends NewScanners with MarkupParsers { ps } -////////// MODIFIERS and ANNOTATIONS ///////////////////////////////////////////////// +/* -------- MODIFIERS and ANNOTATIONS ------------------------------------------- */ private def normalize(mods: Modifiers): Modifiers = if ((mods hasFlag Flags.PRIVATE) && mods.privateWithin != nme.EMPTY.toTypeName) @@ -1629,7 +1629,7 @@ trait Parsers extends NewScanners with MarkupParsers { Annotation(constr, nameValuePairs) setPos pos } -//////// PARAMETERS ////////////////////////////////////////////////////////// +/* -------- PARAMETERS ------------------------------------------- */ /** ParamClauses ::= {ParamClause} [[nl] `(' implicit Params `)'] * ParamClause ::= [nl] `(' [Params] ')' @@ -1807,7 +1807,7 @@ trait Parsers extends NewScanners with MarkupParsers { if (inToken == tok) { inNextToken; typ() } else scalaDot(default.toTypeName) -//////// DEFS //////////////////////////////////////////////////////////////// +/* -------- DEFS ------------------------------------------- */ /** Import ::= import ImportExpr {`,' ImportExpr} @@ -2141,7 +2141,7 @@ trait Parsers extends NewScanners with MarkupParsers { /** TmplDef ::= [case] class ClassDef * | [case] object ObjectDef - * | trait TraitDef + * | [override] trait TraitDef */ def tmplDef(mods: Modifiers): Tree = { if (mods.hasFlag(Flags.LAZY)) syntaxError("classes cannot be lazy", false) @@ -2185,7 +2185,8 @@ trait Parsers extends NewScanners with MarkupParsers { if (mods.hasFlag(Flags.TRAIT)) (Modifiers(Flags.TRAIT), List()) else (accessModifierOpt(), paramClauses(name, implicitClassViews, mods.hasFlag(Flags.CASE))) val thistpe = requiresTypeOpt() - var mods1 = if (inToken == SUBTYPE) mods | Flags.DEFERRED else mods + var mods1 = if (mods.hasFlag( Flags.TRAIT )) if (inToken == SUBTYPE) mods | Flags.DEFERRED else mods + else if (inToken == SUBTYPE) { syntaxError("classes are not allowed to be virtual", false); mods } else mods var template = templateOpt(mods1, name, constrMods withAnnotations constrAnnots, vparamss) if (!thistpe.isEmpty) { if (template.self.isEmpty) { @@ -2267,9 +2268,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] - * Extends ::= extends | `<:' + /** ClassTemplateOpt ::= 'extends' ClassTemplate | [['extends'] TemplateBody] + * TraitTemplateOpt ::= TraitExtends TraitTemplate | [['extends'] TemplateBody] | '<:' TemplateBody + * TraitExtends ::= 'extends' | `<:' */ def templateOpt(mods: Modifiers, name: Name, constrMods: Modifiers, vparamss: List[List[ValDef]]): Template = { val pos = inCurrentPos; @@ -2277,7 +2278,10 @@ trait Parsers extends NewScanners with MarkupParsers { if (inToken == EXTENDS || settings.Xexperimental.value && (mods hasFlag Flags.TRAIT) && inToken == SUBTYPE) { inNextToken template(mods hasFlag Flags.TRAIT) - } else { + } else if ((inToken == SUBTYPE) && (mods hasFlag Flags.TRAIT)) { + inNextToken + template(true) + } else { newLineOptWhenFollowedBy(LBRACE) val (self, body) = templateBodyOpt(false) (List(), List(List()), self, body) @@ -2296,7 +2300,7 @@ trait Parsers extends NewScanners with MarkupParsers { // if (pos == inCurrentPos || inIDE) tree else atPos(pos) {tree} } -////////// TEMPLATES //////////////////////////////////////////////////////////// +/* -------- TEMPLATES ------------------------------------------- */ /** TemplateBody ::= [nl] `{' TemplateStatSeq `}' * @param isPre specifies whether in early initializer (true) or not (false) @@ -2328,7 +2332,7 @@ trait Parsers extends NewScanners with MarkupParsers { body } -/////// STATSEQS ////////////////////////////////////////////////////////////// +/* -------- STATSEQS ------------------------------------------- */ /** Packaging ::= package QualId [nl] `{' TopStatSeq `}' */ diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala index 3da23547af..9e2edfab64 100644 --- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala +++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala @@ -44,7 +44,7 @@ trait Definitions { var NullClass: Symbol = _ var NothingClass: Symbol = _ var SingletonClass: Symbol = _ - lazy val uncheckedStableClass = getClass("scala.uncheckedStable") // todo: move to annotation.unchecked + lazy val uncheckedStableClass = getClass("scala.annotation.unchecked.uncheckedStable") lazy val uncheckedVarianceClass = getClass("scala.annotation.unchecked.uncheckedVariance") lazy val ClassClass: Symbol = getClass(sn.Class) diff --git a/src/compiler/scala/tools/nsc/symtab/Flags.scala b/src/compiler/scala/tools/nsc/symtab/Flags.scala index 4d4bb4c70f..c387feefea 100644 --- a/src/compiler/scala/tools/nsc/symtab/Flags.scala +++ b/src/compiler/scala/tools/nsc/symtab/Flags.scala @@ -1,5 +1,5 @@ /* NSC -- new Scala compiler - * Copyright 2005-2007 LAMP/EPFL + * Copyright 2005-2008 LAMP/EPFL * @author Martin Odersky */ // $Id$ @@ -9,22 +9,23 @@ package scala.tools.nsc.symtab object Flags extends Enumeration { // modifiers - final val IMPLICIT = 0x00000001 - final val FINAL = 0x00000002 + final val IMPLICIT = 0x00000200 + final val FINAL = 0x00000020 final val PRIVATE = 0x00000004 - final val PROTECTED = 0x00000008 + final val PROTECTED = 0x00000001 - final val SEALED = 0x00000010 - final val OVERRIDE = 0x00000020 - final val CASE = 0x00000040 - final val ABSTRACT = 0x00000080 // abstract class, or used in conjunction + final val SEALED = 0x00000400 + final val OVERRIDE = 0x00000002 + final val CASE = 0x00000800 + final val ABSTRACT = 0x00000008 // abstract class, or used in conjunction // with abstract override. // Note difference to DEFERRED! - final val DEFERRED = 0x00000100 // was `abstract' for members - final val METHOD = 0x00000200 // a method - final val MODULE = 0x00000400 // symbol is module or class implementing a module - final val INTERFACE = 0x00000800 // symbol is an interface (i.e. a trait which defines only abstract methods) + final val DEFERRED = 0x00000010 // was `abstract' for members | trait is virtual + final val METHOD = 0x00000040 // a method + final val MODULE = 0x00000100 // symbol is module or class implementing a module + final val INTERFACE = 0x00000080 // symbol is an interface (i.e. a trait which defines only abstract methods) + final val MUTABLE = 0x00001000 // symbol is a mutable variable. final val PARAM = 0x00002000 // symbol is a (value or type) parameter to a method @@ -80,11 +81,11 @@ object Flags extends Enumeration { final val LOCKED = 0x8000000000L // temporary flag to catch cyclic dependencies - final val InitialFlags = 0x000000FFFFFFFFFFL // flags that are enabled from phase 1. - final val LateFlags = 0x000FFF0000000000L // flags that override flags in 0xFFF. - final val AntiFlags = 0x7FF0000000000000L // flags that cancel flags in 0x7FF - final val LateShift = 40L - final val AntiShift = 52L + final val InitialFlags = 0x0001FFFFFFFFFFFFL // flags that are enabled from phase 1. + final val LateFlags = 0x00FE000000000000L // flags that override flags in 0x1FC. + final val AntiFlags = 0x7F00000000000000L // flags that cancel flags in 0x07F + final val LateShift = 47L + final val AntiShift = 56L // late flags (set by a transformer phase) final val latePRIVATE = (PRIVATE: Long) << LateShift @@ -104,6 +105,118 @@ object Flags extends Enumeration { final val notMETHOD = (METHOD: Long) << AntiShift + // The flags from 0x001 to 0x800 are different in the raw flags + // and in the pickled format. + + private final val IMPLICIT_PKL = 0x00000001 + private final val FINAL_PKL = 0x00000002 + private final val PRIVATE_PKL = 0x00000004 + private final val PROTECTED_PKL = 0x00000008 + + private final val SEALED_PKL = 0x00000010 + private final val OVERRIDE_PKL = 0x00000020 + private final val CASE_PKL = 0x00000040 + private final val ABSTRACT_PKL = 0x00000080 + + private final val DEFERRED_PKL = 0x00000100 + private final val METHOD_PKL = 0x00000200 + private final val MODULE_PKL = 0x00000400 + private final val INTERFACE_PKL = 0x00000800 + + private final val PKL_MASK = 0x00000FFF + + + private val r2p = { + def rawFlagsToPickledAux(flags:Int) = { + var pflags=0 + if ((flags & IMPLICIT )!=0) pflags|=IMPLICIT_PKL + if ((flags & FINAL )!=0) pflags|=FINAL_PKL + if ((flags & PRIVATE )!=0) pflags|=PRIVATE_PKL + if ((flags & PROTECTED)!=0) pflags|=PROTECTED_PKL + if ((flags & SEALED )!=0) pflags|=SEALED_PKL + if ((flags & OVERRIDE )!=0) pflags|=OVERRIDE_PKL + if ((flags & CASE )!=0) pflags|=CASE_PKL + if ((flags & ABSTRACT )!=0) pflags|=ABSTRACT_PKL + if ((flags & DEFERRED )!=0) pflags|=DEFERRED_PKL + if ((flags & METHOD )!=0) pflags|=METHOD_PKL + if ((flags & MODULE )!=0) pflags|=MODULE_PKL + if ((flags & INTERFACE)!=0) pflags|=INTERFACE_PKL + pflags + } + val v=new Array[Int](PKL_MASK+1) + var i=0 + while (i<=PKL_MASK) { + v(i)=rawFlagsToPickledAux(i) + i+=1 + } + v + } + + private val p2r = { + def pickledToRawFlagsAux(pflags:Int) = { + var flags=0 + if ((pflags & IMPLICIT_PKL )!=0) flags|=IMPLICIT + if ((pflags & FINAL_PKL )!=0) flags|=FINAL + if ((pflags & PRIVATE_PKL )!=0) flags|=PRIVATE + if ((pflags & PROTECTED_PKL)!=0) flags|=PROTECTED + if ((pflags & SEALED_PKL )!=0) flags|=SEALED + if ((pflags & OVERRIDE_PKL )!=0) flags|=OVERRIDE + if ((pflags & CASE_PKL )!=0) flags|=CASE + if ((pflags & ABSTRACT_PKL )!=0) flags|=ABSTRACT + if ((pflags & DEFERRED_PKL )!=0) flags|=DEFERRED + if ((pflags & METHOD_PKL )!=0) flags|=METHOD + if ((pflags & MODULE_PKL )!=0) flags|=MODULE + if ((pflags & INTERFACE_PKL)!=0) flags|=INTERFACE + flags + } + val v=new Array[Int](PKL_MASK+1) + var i=0 + while (i<=PKL_MASK) { + v(i)=pickledToRawFlagsAux(i) + i+=1 + } + v + } + + def rawFlagsToPickled(flags:Long):Long = + (flags & ~PKL_MASK) | r2p(flags.toInt & PKL_MASK) + + def pickledToRawFlags(pflags:Long):Long = + (pflags & ~PKL_MASK) | p2r(pflags.toInt & PKL_MASK) + + // List of the raw flags, in pickled order + private val pickledListOrder = { + def findBit(m:Long):Int = { + var mask=m + var i=0 + while (i <= 62) { + if ((mask&1) == 1) return i + mask >>= 1 + i += 1 + } + throw new FatalError("Internal error: mask is zero") + } + val v=new Array[Long](63) + v(findBit(IMPLICIT_PKL ))=IMPLICIT + v(findBit(FINAL_PKL ))=FINAL + v(findBit(PRIVATE_PKL ))=PRIVATE + v(findBit(PROTECTED_PKL))=PROTECTED + v(findBit(SEALED_PKL ))=SEALED + v(findBit(OVERRIDE_PKL ))=OVERRIDE + v(findBit(CASE_PKL ))=CASE + v(findBit(ABSTRACT_PKL ))=ABSTRACT + v(findBit(DEFERRED_PKL ))=DEFERRED + v(findBit(METHOD_PKL ))=METHOD + v(findBit(MODULE_PKL ))=MODULE + v(findBit(INTERFACE_PKL))=INTERFACE + var i=findBit(PKL_MASK+1) + while (i <= 62) { + v(i)=1L << i + i += 1 + } + v.toList + } + // masks /** This flags can be set when class or module symbol is first created. */ final val TopLevelCreationFlags: Long = @@ -134,7 +247,7 @@ object Flags extends Enumeration { ss.filter("" !=).mkString("", " ", "") def flagsToString(flags: Long): String = - listToString(for (i <- List.range(0, 63)) yield flagToString(flags & (1L << i))) + listToString(for (mask <- pickledListOrder) yield flagToString(flags & mask)) def flagsToString(flags: Long, privateWithin: String): String = { var f = flags diff --git a/src/compiler/scala/tools/nsc/symtab/Types.scala b/src/compiler/scala/tools/nsc/symtab/Types.scala index 81a4258b06..85e19a2b6b 100644 --- a/src/compiler/scala/tools/nsc/symtab/Types.scala +++ b/src/compiler/scala/tools/nsc/symtab/Types.scala @@ -976,6 +976,7 @@ trait Types { underlyingCache } + override def isVolatile : Boolean = underlying.isVolatile && (!sym.isStable) /* override def narrow: Type = { if (phase.erasedTypes) this diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala index c318c08ffb..1d1dc2e857 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala @@ -1,5 +1,5 @@ /* NSC -- new Scala compiler - * Copyright 2005-2007 LAMP/EPFL + * Copyright 2005-2008 LAMP/EPFL * @author Martin Odersky */ // $Id$ @@ -470,7 +470,7 @@ abstract class Pickler extends SubComponent { var posOffset = 0 writeRef(sym.name) writeRef(normalizedOwner(sym)) - writeNat((sym.flags & PickledFlags).asInstanceOf[Int]) + writeNat((rawFlagsToPickled(sym.flags & PickledFlags)).asInstanceOf[Int]) if (sym.privateWithin != NoSymbol) writeRef(sym.privateWithin) writeRef(sym.info) posOffset @@ -917,8 +917,9 @@ abstract class Pickler extends SubComponent { case Modifiers(flags, privateWithin, annotations) => - writeNat((flags >> 32).toInt) - writeNat((flags & 0xFFFFFFFF).toInt) + val pflags = rawFlagsToPickled(flags) + writeNat((pflags >> 32).toInt) + writeNat((pflags & 0xFFFFFFFF).toInt) writeRef(privateWithin) writeRefs(annotations) MODIFIERS diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/UnPickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/UnPickler.scala index 625dd820ed..76aa52b306 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/UnPickler.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/UnPickler.scala @@ -182,7 +182,7 @@ abstract class UnPickler { } val name = readNameRef() val owner = readSymbolRef() - val flags = readNat() + val flags = pickledToRawFlags(readNat()) var privateWithin: Symbol = NoSymbol var inforef = readNat() if (isSymbolRef(inforef)) { @@ -701,9 +701,10 @@ abstract class UnPickler { if (tag != MODIFIERS) errorBadSignature("expected a modifiers tag (" + tag + ")") val end = readNat() + readIndex - val flagsHi = readNat() - val flagsLo = readNat() - val flags = (flagsHi.toLong << 32) + flagsLo + val pflagsHi = readNat() + val pflagsLo = readNat() + val pflags = (pflagsHi.toLong << 32) + pflagsLo + val flags = pickledToRawFlags(pflags) val privateWithin = readNameRef() val annotations = until(end, readAnnotationTreeRef) Modifiers(flags, privateWithin, annotations) diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 54ddcdb25d..d3f6689b6e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -1,5 +1,5 @@ /* NSC -- new Scala compiler - * Copyright 2005-2007 LAMP/EPFL + * Copyright 2005-2008 LAMP/EPFL * @author Martin Odersky */ // $Id$ @@ -935,6 +935,7 @@ trait Namers { self: Analyzer => Flags.flagsToString(flag1) + " and " + Flags.flagsToString(flag2) + " for: " + sym + Flags.flagsToString(sym.rawflags)); } + if (sym.hasFlag(IMPLICIT) && !sym.isTerm) context.error(sym.pos, "`implicit' modifier can be used only for values, variables and methods") if (sym.hasFlag(IMPLICIT) && sym.owner.isPackageClass && !inIDE) @@ -942,7 +943,7 @@ trait Namers { self: Analyzer => if (sym.hasFlag(ABSTRACT) && !sym.isClass) context.error(sym.pos, "`abstract' modifier can be used only for classes; " + "\nit should be omitted for abstract members") - if (sym.hasFlag(OVERRIDE | ABSOVERRIDE) && sym.isClass) + if (sym.hasFlag(OVERRIDE | ABSOVERRIDE) && !sym.hasFlag(TRAIT) && sym.isClass) context.error(sym.pos, "`override' modifier not allowed for classes") if (sym.hasFlag(OVERRIDE | ABSOVERRIDE) && sym.isConstructor) context.error(sym.pos, "`override' modifier not allowed for constructors") @@ -964,11 +965,12 @@ trait Namers { self: Analyzer => sym.resetFlag(DEFERRED) } } + checkNoConflict(DEFERRED, PRIVATE) checkNoConflict(FINAL, SEALED) checkNoConflict(PRIVATE, PROTECTED) checkNoConflict(PRIVATE, OVERRIDE) - //checkNoConflict(PRIVATE, FINAL) // can't do this because FINAL also means compile-time constant + /* checkNoConflict(PRIVATE, FINAL) // can't do this because FINAL also means compile-time constant */ checkNoConflict(DEFERRED, FINAL) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index e18b3f6130..d1f0edc7c1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -134,7 +134,7 @@ abstract class RefChecks extends InfoTransform { def overrideError(msg: String) { if (other.tpe != ErrorType && member.tpe != ErrorType) - unit.error(pos, "error overriding " + infoStringWithLocation(other) + + unit.error(pos, "overriding " + infoStringWithLocation(other) + ";\n " + infoString(member) + " " + msg + (if ((other.owner isSubClass member.owner) && other.isDeferred && !member.isDeferred) @@ -146,7 +146,7 @@ abstract class RefChecks extends InfoTransform { def overrideTypeError() { if (other.tpe != ErrorType && member.tpe != ErrorType) { - overrideError("has incompatible type "+analyzer.underlying(member).tpe.normalize) + overrideError("has incompatible type") } } @@ -455,8 +455,9 @@ abstract class RefChecks extends InfoTransform { case ExistentialType(tparams, result) => validateVariances(tparams map (_.info), variance) validateVariance(result, variance) - case AnnotatedType(attribs, tp, selfsym) => - validateVariance(tp, variance) + case AnnotatedType(annots, tp, selfsym) => + if (!(annots exists (_.atp.typeSymbol.isNonBottomSubClass(uncheckedVarianceClass)))) + validateVariance(tp, variance) } def validateVariances(tps: List[Type], variance: Int) { diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 3ca63f55d9..209760a66e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1033,7 +1033,8 @@ trait Typers { self: Analyzer => } if (!(selfType <:< parent.tpe.typeOfThis) && !phase.erasedTypes && - !(context.owner hasFlag SYNTHETIC)) // don't do this check for synthetic concrete classes for virtuals (part of DEVIRTUALIZE) + !(context.owner hasFlag SYNTHETIC) && // don't do this check for synthetic concrete classes for virtuals (part of DEVIRTUALIZE) + !(settings.suppressVTWarn.value)) { //Console.println(context.owner);//DEBUG //Console.println(context.owner.unsafeTypeParams);//DEBUG @@ -3344,7 +3345,7 @@ trait Typers { self: Analyzer => tree.tpe = null if (tree.hasSymbol) tree.symbol = NoSymbol } - if (printTypings) println("typing "+tree+", "+context.undetparams+(mode & TYPEPATmode));//DEBUG + if (printTypings) println("typing "+tree+", "+context.undetparams+(mode & TYPEPATmode)); //DEBUG def dropExistential(tp: Type): Type = tp match { case ExistentialType(tparams, tpe) => if (settings.debug.value) println("drop ex "+tree+" "+tp) @@ -3356,12 +3357,12 @@ trait Typers { self: Analyzer => case _ => tp } var tree1 = if (tree.tpe ne null) tree else typed1(tree, mode, dropExistential(pt)) - if (printTypings) println("typed "+tree1+":"+tree1.tpe+", "+context.undetparams+", pt = "+pt);//DEBUG + if (printTypings) println("typed "+tree1+":"+tree1.tpe+", "+context.undetparams+", pt = "+pt); //DEBUG tree1.tpe = addAnnotations(tree1, tree1.tpe) val result = if (tree1.isEmpty || (inIDE && tree1.tpe == null)) tree1 else adapt(tree1, mode, pt) - if (printTypings) println("adapted "+tree1+":"+tree1.tpe+" to "+pt+", "+context.undetparams);//DEBUG + if (printTypings) println("adapted "+tree1+":"+tree1.tpe+" to "+pt+", "+context.undetparams); //DEBUG // if ((mode & TYPEmode) != 0) println("type: "+tree1+" has type "+tree1.tpe) result } catch { diff --git a/src/compiler/scala/tools/nsc/util/ShowPickled.scala b/src/compiler/scala/tools/nsc/util/ShowPickled.scala index 3000e6d61a..7cbc21b814 100644 --- a/src/compiler/scala/tools/nsc/util/ShowPickled.scala +++ b/src/compiler/scala/tools/nsc/util/ShowPickled.scala @@ -1,5 +1,5 @@ /* NSC -- new Scala compiler - * Copyright 2005-2007 LAMP/EPFL + * Copyright 2005-2008 LAMP/EPFL * @author Martin Odersky */ // $Id$ @@ -84,9 +84,9 @@ object ShowPickled extends Names { def printSymInfo() { printNameRef() printSymbolRef() - val flags = buf.readNat() - out.print(" " + toHexString(flags) + - "[" + Flags.flagsToString(flags) + "] ") + val pflags = buf.readNat() + out.print(" " + toHexString(pflags) + + "[" + Flags.flagsToString(Flags.pickledToRawFlags(pflags)) + "] ") printTypeRef() } diff --git a/src/library/scala/annotation/unchecked/uncheckedStable.scala b/src/library/scala/annotation/unchecked/uncheckedStable.scala new file mode 100755 index 0000000000..d6fcdf2127 --- /dev/null +++ b/src/library/scala/annotation/unchecked/uncheckedStable.scala @@ -0,0 +1,13 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ +package scala.annotation.unchecked + +/** An annotation for values that are assumed to be stable even though their + * types are volatile. + */ +final class uncheckedStable extends StaticAnnotation {} diff --git a/src/library/scala/annotation/unchecked/uncheckedVariance.scala b/src/library/scala/annotation/unchecked/uncheckedVariance.scala index 862db90809..1cb9a82d6b 100644 --- a/src/library/scala/annotation/unchecked/uncheckedVariance.scala +++ b/src/library/scala/annotation/unchecked/uncheckedVariance.scala @@ -1,6 +1,6 @@ /* __ *\ ** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2007, LAMP/EPFL ** +** / __/ __// _ | / / / _ | (c) 2002-2008, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** diff --git a/src/library/scala/collection/mutable/ArrayBuffer.scala b/src/library/scala/collection/mutable/ArrayBuffer.scala index eb3f8a07ce..74bef697bc 100644 --- a/src/library/scala/collection/mutable/ArrayBuffer.scala +++ b/src/library/scala/collection/mutable/ArrayBuffer.scala @@ -1,6 +1,6 @@ /* __ *\ ** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2007, LAMP/EPFL ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** diff --git a/src/library/scala/collection/mutable/ResizableArray.scala b/src/library/scala/collection/mutable/ResizableArray.scala index 2e2e51ecb9..211d785a61 100644 --- a/src/library/scala/collection/mutable/ResizableArray.scala +++ b/src/library/scala/collection/mutable/ResizableArray.scala @@ -1,6 +1,6 @@ /* __ *\ ** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2007, LAMP/EPFL ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** @@ -74,7 +74,7 @@ trait ResizableArray[A] extends RandomAccessSeq[A] { /** ensure that the internal array has at n cells */ protected def ensureSize(n: Int) { if (n > array.length) { - var newsize = array.length * 2 + var newsize = if (array.length == 0) 2 else array.length * 2 while (n > newsize) newsize = newsize * 2 val newar: Array[AnyRef] = new Array(newsize) diff --git a/src/library/scalax/Fractional.scala b/src/library/scalax/Fractional.scala new file mode 100755 index 0000000000..b29a903519 --- /dev/null +++ b/src/library/scalax/Fractional.scala @@ -0,0 +1,5 @@ +package scalax + +trait Fractional[T] extends Numeric[T] { + def div(x: T, y: T): T +} diff --git a/src/library/scalax/Integral.scala b/src/library/scalax/Integral.scala new file mode 100755 index 0000000000..2e80b1bb7b --- /dev/null +++ b/src/library/scalax/Integral.scala @@ -0,0 +1,6 @@ +package scalax + +trait Integral[T] extends Numeric[T] { + def quot(x: T, y: T): T + def rem(x: T, y: T): T +} diff --git a/src/library/scalax/Numeric.scala b/src/library/scalax/Numeric.scala new file mode 100755 index 0000000000..6e61851dc5 --- /dev/null +++ b/src/library/scalax/Numeric.scala @@ -0,0 +1,79 @@ +package scalax + +object Numeric { + implicit object IntIsIntegral extends Integral[Int] { + def plus(x: Int, y: Int): Int = x + y + def minus(x: Int, y: Int): Int = x - y + def times(x: Int, y: Int): Int = x * y + def quot(x: Int, y: Int): Int = x / y + def rem(x: Int, y: Int): Int = x % y + def negate(x: Int): Int = -x + def abs(x: Int): Int = if (x < 0) -x else x + def signum(x: Int): Int = if (x < 0) -1 else if (x > 0) 1 else 0 + def fromInt(x: Int): Int = x + def toInt(x: Int): Int = x + def toLong(x: Int): Long = x + def toFloat(x: Int): Float = x + def toDouble(x: Int): Double = x + } + implicit object LongIsIntegral extends Integral[Long] { + def plus(x: Long, y: Long): Long = x + y + def minus(x: Long, y: Long): Long = x - y + def times(x: Long, y: Long): Long = x * y + def quot(x: Long, y: Long): Long = x / y + def rem(x: Long, y: Long): Long = x % y + def negate(x: Long): Long = -x + def abs(x: Long): Long = if (x < 0) -x else x + def signum(x: Long): Long = if (x < 0) -1 else if (x > 0) 1 else 0 + def fromInt(x: Int): Long = x + def toInt(x: Long): Int = x.toInt + def toLong(x: Long): Long = x + def toFloat(x: Long): Float = x + def toDouble(x: Long): Double = x + } + implicit object FloatIsFractional extends Fractional[Float] { + def plus(x: Float, y: Float): Float = x + y + def minus(x: Float, y: Float): Float = x - y + def times(x: Float, y: Float): Float = x * y + def div(x: Float, y: Float): Float = x / y + def negate(x: Float): Float = -x + def abs(x: Float): Float = if (x < 0) -x else x + def signum(x: Float): Float = if (x < 0) -1 else if (x > 0) 1 else 0 + def fromInt(x: Int): Float = x + def toInt(x: Float): Int = x.toInt + def toLong(x: Float): Long = x.toLong + def toFloat(x: Float): Float = x + def toDouble(x: Float): Double = x + } + implicit object DoubleIsFractional extends Fractional[Double] { + def plus(x: Double, y: Double): Double = x + y + def minus(x: Double, y: Double): Double = x - y + def times(x: Double, y: Double): Double = x * y + def div(x: Double, y: Double): Double = x / y + def negate(x: Double): Double = -x + def abs(x: Double): Double = if (x < 0) -x else x + def signum(x: Double): Double = if (x < 0) -1 else if (x > 0) 1 else 0 + def fromInt(x: Int): Double = x + def toInt(x: Double): Int = x.toInt + def toLong(x: Double): Long = x.toLong + def toFloat(x: Double): Float = x.toFloat + def toDouble(x: Double): Double = x + } +} + + +trait Numeric[T] { + def plus(x: T, y: T): T + def minus(x: T, y: T): T + def times(x: T, y: T): T + def negate(x: T): T + def abs(x: T): T + def signum(x: T): T + def fromInt(x: Int): T + def toInt(x: T): Int + def toLong(x: T): Long + def toFloat(x: T): Float + def toDouble(x: T): Double + def zero = fromInt(0) + def one = fromInt(1) +} diff --git a/src/library/scalax/collection/BufferedIterator.scala b/src/library/scalax/collection/BufferedIterator.scala new file mode 100755 index 0000000000..07509c2758 --- /dev/null +++ b/src/library/scalax/collection/BufferedIterator.scala @@ -0,0 +1,27 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2007, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: BufferedIterator.scala 12641 2007-08-22 16:01:57Z mcdirmid $ + + +package scalax.collection + +/** Buffered iterators are iterators which allow to inspect the next + * element without discarding it. + * + * @author Martin Odersky + * @version 2.8 + */ +trait BufferedIterator[+A] extends Iterator[A] { + + /** Returns current element of iterator without advancing beyond it. + */ + def head: A + + override def buffered: this.type = this +} diff --git a/src/library/scalax/collection/Builder.scala b/src/library/scalax/collection/Builder.scala new file mode 100755 index 0000000000..eedd89537a --- /dev/null +++ b/src/library/scalax/collection/Builder.scala @@ -0,0 +1,21 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2007, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: ListBuffer.scala 14378 2008-03-13 11:39:05Z dragos $ + +package scalax.collection + + +trait Builder[+CC[B], A] extends mutable.Appendable[A] { + def +=(x: A) + def elements: Iterator[A] + def result: CC[A] + override def ++=(xs: Iterator[A]) { for (x <- xs) this += x } + override def ++=(xs: Iterable[A]) { for (x <- xs) this += x } +} + diff --git a/src/library/scalax/collection/Iterable.scala b/src/library/scalax/collection/Iterable.scala new file mode 100755 index 0000000000..b6abd8d559 --- /dev/null +++ b/src/library/scalax/collection/Iterable.scala @@ -0,0 +1,143 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Iterable.scala 15188 2008-05-24 15:01:02Z stepancheg $ + + +package scalax.collection + +import generic._ + +/** Collection classes mixing in this class provide a method + * elements which returns an iterator over all the + * elements contained in the collection. + * + * @note If a collection has a known size, it should also sub-type SizedIterable. + * + * @author Matthias Zenger + * @autor Martin Odersky + * @owner Martin Odersky + * @version 2.8 + */ +trait Iterable[+A] extends covariant.IterableTemplate[Iterable, A] { self => + + /** Creates a view of this iterable @see Iterable.View + def view: View[Iterable, A] = new View[Iterable, A] { // !!! Martin: We should maybe infer the type parameters here? + val origin: self.type = self + val elements: Iterator[A] = self.elements + } + */ +} + +/** Various utilities for instances of Iterable. + * + * @author Matthias Zenger + * @author Martin Odersky + * @version 2.8 + */ +object Iterable extends covariant.IterableFactory[Iterable] { + + /** The empty iterable */ + val empty: Iterable[Nothing] = null // !!! + + class OrderedIterableOps[A](seq: Iterable[A], cmp: Ordering[A]) { + def min: A = { + require(!seq.isEmpty, "min()") + var acc = seq.elements.next + for (x <- seq) + if (cmp.lt(x, acc)) acc = x + acc + } + def max: A = { + require(!seq.isEmpty, "max()") + var acc = seq.elements.next + for (x <- seq) + if (cmp.gt(x, acc)) acc = x + acc + } + } + + class NumericIterableOps[A](seq: Iterable[A], num: Numeric[A]) { + def sum: A = { + var acc = num.zero + for (x <- seq) acc = num.plus(acc, x) + acc + } + def product: A = { + var acc = num.one + for (x <- seq) acc = num.times(acc, x) + acc + } + } + + class IterableIterableOps[C[+B] <: Iterable[B], A](self: C[Iterable[A]]) { + def flatten: C[A] = { + val b = self.newBuilder[A].asInstanceOf[Builder[C, A]] + for (xs <- self) + b ++= xs + b.result + } + } + + class PairCollectionOps[C[+B] <: Iterable[B], A1, A2](self: C[(A1, A2)]) { + def unzip: (C[A1], C[A2]) = { + val as = self.newBuilder[A1].asInstanceOf[Builder[C, A1]] + val bs = self.newBuilder[A2].asInstanceOf[Builder[C, A2]] + for ((a, b) <- self) { + as += a + bs += b + } + (as.result, bs.result) + } + } + + implicit def orderedIterableWrapper[A](seq: Iterable[A])(implicit cmp: Ordering[A]) = + new OrderedIterableOps(seq, cmp) + implicit def numericIterableWrapper[A](seq: Iterable[A])(implicit num: Numeric[A]) = + new NumericIterableOps(seq, num) + implicit def iterableIterableWrapper[C[+B] <: Iterable[B], A](seq: C[Iterable[A]]) = + new IterableIterableOps[C, A](seq) + implicit def pairCollectionWrapper[C[+B] <: Iterable[B], A1, A2](seq: C[(A1, A2)]) = + new PairCollectionOps[C, A1, A2](seq) + + type View[+UC[+B] <: Sequence[B], +A] = covariant.IterableView[UC, A] + + /** @deprecated use View instead + */ + @deprecated type Projection[+A] = View[C, A] forSome { type C[B] <: Iterable[B] } + + /** The minimum element of a non-empty sequence of ordered elements + * @deprecated use seq.min instead + */ + @deprecated def min[A <% Ordered[A]](seq: Iterable[A]): A = { + val xs = seq.elements + if (!xs.hasNext) throw new IllegalArgumentException("min()") + var min = xs.next + while (xs.hasNext) { + val x = xs.next + if (x < min) min = x + } + min + } + + /** The maximum element of a non-empty sequence of ordered elements + * @deprecated use seq.max iConstead + */ + @deprecated def max[A <% Ordered[A]](seq: Iterable[A]): A = { + val xs = seq.elements + if (!xs.hasNext) throw new IllegalArgumentException("max()") + var max = xs.next + while (xs.hasNext) { + val x = xs.next + if (max < x) max = x + } + max + } + +} + diff --git a/src/library/scalax/collection/Iterator.scala b/src/library/scalax/collection/Iterator.scala new file mode 100755 index 0000000000..2758faf854 --- /dev/null +++ b/src/library/scalax/collection/Iterator.scala @@ -0,0 +1,970 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Iterator.scala 15939 2008-08-26 14:33:17Z stepancheg $ + + +package scalax.collection + +import mutable.{Buffer, ArrayBuffer, ListBuffer} +import immutable.{List, Nil, ::} + +/** The Iterator object provides various functions for + * creating specialized iterators. + * + * @author Martin Odersky + * @author Matthias Zenger + * @version 2.8 + */ +object Iterator { + + val empty = new Iterator[Nothing] { + def hasNext: Boolean = false + def next(): Nothing = throw new NoSuchElementException("next on empty iterator") + } + + def apply[A](args: A*): Iterator[A] = args.asInstanceOf[Iterable[A]].elements // !!! + + /** Concatenate all the argument iterators into a single iterator. + * + * @param xss the lists that are to be concatenated + * @return the concatenation of all the lists + */ + def concat[A](xss: Iterator[A]*): Iterator[A] = + xss.asInstanceOf[Iterable[Iterator[A]]].elements.flatten // !!! + + /** An iterator that returns the same element a number of times + * @param len The number of elements returned + * @param elem The element returned each time + */ + def fill[A](len: Int, elem: => A) = new Iterator[A] { + private var i = 0 + def hasNext: Boolean = i < len + def next(): A = + if (hasNext) { i += 1; elem } + else empty.next() + } + + /** An iterator that returns values of a function f(0), ..., f(n-1), + * for given `f` and `n`. + */ + def tabulate[A](n: Int)(f: Int => A) = new Iterator[A] { + private var i = 0 + def hasNext: Boolean = i < n + def next(): A = + if (hasNext) { val result = f(i); i += 1; result } + else empty.next() + } + + /** Nn iterator with elements + * en+1 = en + 1 + * where e0 = start + * and ei < end. However, + * if start ≥ end, then it will return an empty range. + * + * @param start the start value of the iterator + * @param end the end value of the iterator + * @return the iterator with values in range [start;end). + */ + def range(start: Int, end: Int): Iterator[Int] = range(start, end, 1) + + /** Create an iterator with elements + * en+1 = en + step + * where e0 = start + * and elements are in the range between start (inclusive) + * and end (exclusive) + * + * @param start the start value of the iterator + * @param end the end value of the iterator + * @param step the increment value of the iterator (must be positive or negative) + * @return the iterator with values in range [start;end). + */ + def range(start: Int, end: Int, step: Int) = new Iterator[Int] { + private var i = start + def hasNext: Boolean = (step <= 0 || i < end) && (step >= 0 || i > end) + def next(): Int = + if (hasNext) { val result = i; i += step; result } + else empty.next() + } + + /** An iterator that repeatedly applies a given function to a start value. + * + * @param start the start value of the iteratpr + * @param len the number of elements returned by the iterator + * @param f the function that's repeatedly applied + * @return the iterator returning values (start, f(start), f(f(start)), ..., flen-1(start)) + */ + def iterate(start: Int, len: Int)(f: Int => Int) = new Iterator[Int] { + private var acc = start + private var i = 0 + def hasNext: Boolean = i < len + def next(): Int = + if (hasNext) { val result = f(acc); i += 1; result } + else empty.next() + } + + /** An infinite iterator that repeatedly applies a given function to a start value. + * + * @param start the start value of the iteratpr + * @param f the function that's repeatedly applied + * @return the iterator returning values (start, f(start), f(f(start)), ..., flen-1(start)) + */ + def iterate(start: Int)(f: Int => Int) = new Iterator[Int] { + private var acc = start + private var i = 0 + def hasNext: Boolean = true + def next(): Int = { val result = f(acc); i += 1; result } + } + + /** Create an iterator with elements + * en+1 = en + 1 + * where e0 = start. + * + * @param start the start value of the iterator + * @return the iterator starting at value start. + */ + def from(start: Int): Iterator[Int] = from(start, 1) + + /** Create an iterator with elements + * en+1 = en + step + * where e0 = start. + * + * @param start the start value of the iterator + * @param step the increment value of the iterator + * @return the iterator starting at value start. + */ + def from(start: Int, step: Int): Iterator[Int] = new Iterator[Int] { + private var i = 0 + def hasNext: Boolean = true + def next(): Int = { val result = i; i += 1; result } + } + + class IteratorIteratorOps[A](its: Iterator[Iterator[A]]) { + /** Create an iterator that is the concantenation of all iterators + * returned by a given iterator of iterators. + * @param its The iterator which returns on each call to next + * a new iterator whose elements are to be concatenated to the result. + */ + def flatten: Iterator[A] = new Iterator[A] { + private var it = its.next + def hasNext: Boolean = { + while (!it.hasNext && its.hasNext) it = its.next + it.hasNext + } + def next(): A = + if (hasNext) it.next + else empty.next() + } + } + + implicit def iteratorIteratorWrapper[A](its: Iterator[Iterator[A]]): IteratorIteratorOps[A] = + new IteratorIteratorOps[A](its) + + /** + * @param x the element + * @return the iterator with one single element + * @deprecated use Iterator(x) instead + */ + @deprecated def single[a](x: a) = new Iterator[a] { + private var hasnext = true + def hasNext: Boolean = hasnext + def next(): a = + if (hasnext) { hasnext = false; x } + else empty.next() + } + + /** @deprecated use `xs.elements` instead + */ + @deprecated def fromValues[a](xs: a*) = xs.elements + + /** + * @param xs the array of elements + * @see also: Vector.elements and slice + * @deprecated use `xs.elements` instead + */ + @deprecated def fromArray[a](xs: Array[a]): Iterator[a] = + fromArray(xs, 0, xs.length) + + /** + * @param xs the array of elements + * @param start the start index + * @param length the length + * @see also: Vector.elements and slice + * @deprecated use `xs.slice(start, start + length).elements` instead + */ + @deprecated def fromArray[a](xs: Array[a], start: Int, length: Int): Iterator[a] = + xs.slice(start, start + length).elements.asInstanceOf[Iterator[a]] // !!! + + /** + * @param str the given string + * @return the iterator on str + * @deprecated replaced by str.elements + */ + @deprecated def fromString(str: String): Iterator[Char] = + str.elements.asInstanceOf[Iterator[Char]] // !!! + + /** + * @param n the product arity + * @return the iterator on Product<n>. + * @deprecated use product.productElements instead + */ + @deprecated def fromProduct(n: Product): Iterator[Any] = new Iterator[Any] { + private var c: Int = 0 + private val cmax = n.productArity + def hasNext = c < cmax + def next() = { val a = n productElement c; c += 1; a } + } + + /** Create an iterator with elements + * en+1 = step(en) + * where e0 = start + * and elements are in the range between start (inclusive) + * and end (exclusive) + * + * @param start the start value of the iterator + * @param end the end value of the iterator + * @param step the increment function of the iterator, must be monotonically increasing or decreasing + * @return the iterator with values in range [start;end). + * @deprecated use Iterator.iterate(start, end - start)(step) instead + */ + @deprecated def range(start: Int, end: Int, step: Int => Int) = new Iterator[Int] { + private val up = step(start) > start + private val down = step(start) < start + private var i = start + def hasNext: Boolean = (!up || i < end) && (!down || i > end) + def next(): Int = + if (hasNext) { val j = i; i = step(i); j } + else empty.next() + } + + /** Create an iterator with elements + * en+1 = step(en) + * where e0 = start. + * + * @param start the start value of the iterator + * @param step the increment function of the iterator + * @return the iterator starting at value start. + * @deprecated use iterate(start)(step) instead + */ + @deprecated def from(start: Int, step: Int => Int): Iterator[Int] = new Iterator[Int] { + private var i = start + override def hasNext: Boolean = true + def next(): Int = { val j = i; i = step(i); j } + } + + /** Create an iterator that is the concantenation of all iterators + * returned by a given iterator of iterators. + * @param its The iterator which returns on each call to next + * a new iterator whose elements are to be concatenated to the result. + * @deprecated use its.flatten instead + */ + @deprecated def flatten[T](its: Iterator[Iterator[T]]): Iterator[T] = new Iterator[T] { + private var it = its.next + def hasNext: Boolean = { + while (!it.hasNext && its.hasNext) it = its.next + it.hasNext + } + def next(): T = + if (hasNext) it.next + else empty.next() + } +} + +import Iterator.empty + +/** Iterators are data structures that allow to iterate over a sequence + * of elements. They have a hasNext method for checking + * if there is a next element available, and a next method + * which returns the next element and discards it from the iterator. + * + * @author Martin Odersky, Matthias Zenger + * @version 2.8 + */ +trait Iterator[+A] { +self => + + /** Does this iterator provide another element? + */ + def hasNext: Boolean + + /** Returns the next element. + */ + def next(): A + + /** Returns a new iterator that iterates only over the first n + * elements. + * + * @param n the number of elements to take + * @return the new iterator + */ + def take(n: Int): Iterator[A] = new Iterator[A] { + private var remaining = n + def hasNext = remaining > 0 && self.hasNext + def next(): A = + if (hasNext) { remaining -= 1; self.next } + else throw new NoSuchElementException("next on empty iterator") + } + + /** Removes the first n elements from this iterator. + * + * @param n the number of elements to drop + * @return the new iterator + */ + def drop(n: Int): Iterator[A] = + if (n > 0 && hasNext) { next(); drop(n - 1) } else this + + /** A sub-iterator of until - from elements + * starting at index from + * + * @param from The index of the first element of the slice + * @param until The index of the element following the slice + */ + def slice(from: Int, until: Int): Iterator[A] = drop(from).take(until - from) + + /** Returns a new iterator that maps all elements of this iterator + * to new elements using function f. + */ + def map[B](f: A => B): Iterator[B] = new Iterator[B] { + def hasNext = self.hasNext + def next() = f(self.next()) + } + + /** Returns a new iterator that first yields the elements of this + * iterator followed by the elements provided by iterator that. + * @deprecated use ++ + */ + def append[B >: A](that: Iterator[B]) = new Iterator[B] { + def hasNext = self.hasNext || that.hasNext + def next() = (if (self.hasNext) self else that).next() + } + + /** Returns a new iterator that first yields the elements of this + * iterator followed by the elements provided by iterator that. + */ + def ++[B >: A](that: => Iterator[B]) = new Iterator[B] { + // optimize a little bit to prevent n log n behavior. + var cur : Iterator[B] = self + def hasNext = cur.hasNext || (cur eq self) && { cur = that; hasNext } + def next() = { hasNext; cur.next } + } + + /** Applies the given function f to each element of + * this iterator, then concatenates the results. + * + * @param f the function to apply on each element. + * @return an iterator over f(a0), ... , + * f(an) if this iterator yields the + * elements a0, ..., an. + */ + def flatMap[B](f: A => Iterator[B]): Iterator[B] = new Iterator[B] { + private var cur: Iterator[B] = empty + def hasNext: Boolean = + cur.hasNext || self.hasNext && { cur = f(self.next); hasNext } + def next(): B = (if (hasNext) cur else empty).next() + } + + /** Returns an iterator over all the elements of this iterator that + * satisfy the predicate p. The order of the elements + * is preserved. + * + * @param p the predicate used to filter the iterator. + * @return the elements of this iterator satisfying p. + */ + def filter(p: A => Boolean): Iterator[A] = { + val self = buffered + new Iterator[A] { + private def skip() = while (self.hasNext && !p(self.head)) self.next() + def hasNext = { skip(); self.hasNext } + def next() = { skip(); self.next() } + } + } + + /** Returns an iterator over the longest prefix of this iterator such that + * all elements of the result satisfy the predicate p. + * The order of the elements is preserved. + * + * The behavior of this iterator is undefined after this method invocation. + * + * @param p the predicate used to filter the iterator. + * @return the longest prefix of this iterator satisfying p. + */ + def takeWhile(p: A => Boolean): Iterator[A] = { + val self = buffered + new Iterator[A] { + def hasNext = { self.hasNext && p(self.head) } + def next() = (if (hasNext) self else empty).next() + } + } + + /** Partitions this iterator in two iterators according to a predicate. + * + * @param p the predicate on which to partition + * @return a pair of iterators: the iterator that satisfies the predicate + * p and the iterator that does not. + * The relative order of the elements in the resulting iterators + * is the same as in the original iterator. + */ + def partition(p: A => Boolean): (Iterator[A], Iterator[A]) = { + val self = buffered + class PartitionIterator(p: A => Boolean) extends Iterator[A] { + var other: PartitionIterator = _ + val lookahead = new scala.collection.mutable.Queue[A] + def skip() = + while (self.hasNext && !p(self.head)) { + other.lookahead += self.next + } + def hasNext = !lookahead.isEmpty || self.hasNext + def next() = if (lookahead.isEmpty) self.next() else lookahead.dequeue() + } + val l = new PartitionIterator(p) + val r = new PartitionIterator(!p(_)) + l.other = r + r.other = l + (l, r) + } + + /** Skips longest sequence of elements of this iterator which satisfy given + * predicate p, and returns an iterator of the remaining elements. + * + * The behavior of this iterator is undefined after this method invocation. + * + * @param p the predicate used to skip elements. + * @return an iterator consisting of the remaining elements + */ + def dropWhile(p: A => Boolean): Iterator[A] = { + val self = buffered + new Iterator[A] { + var dropped = false + private def skip() = + if (!dropped) { + while (self.hasNext && p(self.head)) self.next() + dropped = true + } + def hasNext = { skip(); self.hasNext } + def next() = { skip(); self.next() } + } + } + + /** Return an iterator formed from this iterator and the specified iterator + * that by associating each element of the former with + * the element at the same position in the latter. + * If one of the two iterators is longer than the other, its remaining elements are ignored. + * + * @return an iterator yielding {a0,b0}, + * {a1,b1}, ... where + * ai are the elements from this iterator + * and bi are the elements from iterator + * that. + */ + def zip[B](that: Iterator[B]) = new Iterator[(A, B)] { + def hasNext = self.hasNext && that.hasNext + def next = (self.next, that.next) + } + + /** Return an iterator that pairs each element of this iterator + * with its index, counting from 0. + * + * @return an iterator yielding {a0,0}, + * {a1,1}... where ai + * are the elements from this iterator. + */ + def zipWithIndex = new Iterator[(A, Int)] { + var idx = 0 + def hasNext = self.hasNext + def next = { + val ret = (self.next, idx) + idx += 1 + ret + } + } + + /** Returns an iterator formed from this iterator and the specified iterator + * that by associating each element of the former with + * the element at the same position in the latter. + * + * @param that iterator that may have a different length + * as the self iterator. + * @param thisElem element thisElem is used to fill up the + * resulting iterator if the self iterator is shorter than + * that + * @param thatElem element thatElem is used to fill up the + * resulting iterator if that is shorter than + * the self iterator + * @return Iterator((a0,b0), ..., + * (an,bn), (elem,bn+1), + * ..., {elem,bm}) + * when [a0, ..., an] zip + * [b0, ..., bm] is + * invoked where m > n. + */ + def zipAll[B, A1 >: A, B1 >: B](that: Iterator[B], thisElem: A1, thatElem: B1) = new Iterator[(A1, B1)] { + def hasNext = self.hasNext || that.hasNext + def next(): (A1, B1) = + if (self.hasNext) { + if (that.hasNext) (self.next(), that.next()) + else (self.next(), thatElem) + } else { + if (that.hasNext) (thisElem, that.next()) + else empty.next() + } + } + + /** Apply a function f to all elements of this + * iterable object. + * + * @param f a function that is applied to every element. + */ + def foreach(f: A => Unit) { while (hasNext) f(next()) } + + /** Apply a predicate p to all elements of this + * iterable object and return true iff the predicate yields + * true for all elements. + * + * @param p the predicate + * @return true iff the predicate yields true + * for all elements. + */ + def forall(p: A => Boolean): Boolean = { + var res = true + while (res && hasNext) res = p(next()) + res + } + + /** Apply a predicate p to all elements of this + * iterable object and return true, iff there is at least one + * element for which p yields true. + * + * @param p the predicate + * @return true iff the predicate yields true + * for at least one element. + */ + def exists(p: A => Boolean): Boolean = { + var res = false + while (!res && hasNext) res = p(next()) + res + } + + /** Tests if the given value elem is a member of this iterator. + * + * @param elem element whose membership has to be tested. + * @return true iff there is an element of this iterator which + * is equal (w.r.t. ==) to elem. + */ + def contains(elem: Any): Boolean = exists { _ == elem } + + /** Find and return the first element of the iterable object satisfying a + * predicate, if any. + * + * @param p the predicate + * @return the first element in the iterable object satisfying + * p, or None if none exists. + */ + def find(p: A => Boolean): Option[A] = { + var res: Option[A] = None + while (res.isEmpty && hasNext) { + val e = next() + if (p(e)) res = Some(e) + } + res + } + + /** Returns index of the first element satisying a predicate, or -1. + * + * @note may not terminate for infinite-sized collections. + * @param p the predicate + * @return the index of the first element satisfying p, + * or -1 if such an element does not exist + */ + def indexWhere(p: A => Boolean): Int = { + var i = 0 + var found = false + while (!found && hasNext) { + if (p(next())) { + found = true + } else { + i += 1 + } + } + if (found) i else -1 + } + + /** Returns index of the first element satisying a predicate, or -1. + * + * @deprecated use `indexWhere` instead + */ + @deprecated def findIndexOf(p: A => Boolean): Int = indexWhere(p) + + /** Returns the index of the first occurence of the specified + * object in this iterable object. + * + * @note may not terminate for infinite-sized collections. + * @param elem element to search for. + * @return the index in this sequence of the first occurence of the + * specified element, or -1 if the sequence does not contain + * this element. + */ + def indexOf[B >: A](elem: B): Int = { + var i = 0 + var found = false + while (!found && hasNext) { + if (next() == elem) { + found = true + } else { + i += 1 + } + } + if (found) i else -1 + } + + /** Combines the elements of this iterator together using the binary + * operator op, from left to right, and starting with + * the value z. + * + * @return op(... (op(op(z,a0),a1) ...), + * an) if the iterator yields elements + * a0, a1, ..., an. + */ + def foldLeft[B](z: B)(op: (B, A) => B): B = { + var acc = z + while (hasNext) acc = op(acc, next()) + acc + } + + /** Combines the elements of this iterator together using the binary + * operator op, from right to left, and starting with + * the value z. + * + * @return a0 op (... op (an op z)...) + * if the iterator yields elements a0, a1, ..., + * an. + */ + def foldRight[B](z: B)(op: (A, B) => B): B = { + def fold(z: B): B = if (hasNext) op(next(), fold(z)) else z + fold(z) + } + + /** Similar to foldLeft but can be used as + * an operator with the order of iterator and zero arguments reversed. + * That is, z /: xs is the same as xs foldLeft z. + * + * @param z the left argument of the first application of op + * (evaluation occurs from left to right). + * @param op the applied operator. + * @return the result value + * @see foldLeft. + */ + def /:[B](z: B)(op: (B, A) => B): B = foldLeft(z)(op) + + /** An alias for foldRight. + * That is, xs :\ z is the same as xs foldRight z. + * + * @param z the right argument of the first application of op + * (evaluation occurs from right to left). + * @param op the applied operator. + * @return the result value. + * @see foldRight. + */ + def :\[B](z: B)(op: (A, B) => B): B = foldRight(z)(op) + + /** Combines the elements of this iterator together using the binary + * operator op, from left to right + * @param op The operator to apply + * @return op(... op(a0,a1), ..., an) + if the iterator yields elements + * a0, a1, ..., an. + * @throws Predef.UnsupportedOperationException if the iterator is empty. + */ + def reduceLeft[B >: A](op: (B, A) => B): B = { + if (hasNext) foldLeft[B](next())(op) + else throw new UnsupportedOperationException("empty.reduceLeft") + } + + /** Combines the elements of this iterator together using the binary + * operator op, from right to left + * @param op The operator to apply + * + * @return a0 op (... op (an-1 op an)...) + * if the iterator yields elements a0, a1, ..., + * an. + + * @throws Predef.UnsupportedOperationException if the iterator is empty. + */ + def reduceRight[B >: A](op: (A, B) => B): B = { + if (!hasNext) throw new UnsupportedOperationException("empty.reduceRight") + foldRight[B](next())(op) + } + + /** Returns a buffered iterator from this iterator. + */ + def buffered = new BufferedIterator[A] { + private var hd: A = _ + private var hdDefined: Boolean = false + + def head: A = { + if (!hdDefined) { + hd = next() + hdDefined = true + } + hd + } + + def hasNext = + hdDefined || self.hasNext + + def next = + if (hdDefined) { + hdDefined = false + hd + } else self.next + } + + def length: Int = { + var i = 0 + while (hasNext) { + next(); i += 1 + } + i + } + + /** Returns a counted iterator from this iterator. + * @deprecated use @see zipWithIndex in Iterator + */ + def counted = new CountedIterator[A] { + private var cnt = -1 + def count = cnt + def hasNext: Boolean = self.hasNext + def next(): A = { cnt += 1; self.next } + } + + /** Creates two new iterators that both iterate over the same elements + * than this iterator (in the same order). + * + * @return a pair of iterators + */ + def duplicate: (Iterator[A], Iterator[A]) = { + var xs: List[A] = Nil + var ahead: Iterator[A] = null + class Partner extends Iterator[A] { + var ys: List[A] = Nil + def hasNext: Boolean = self.synchronized ( + ((this == ahead) && self.hasNext) || + ((this != ahead) && (!xs.isEmpty || !ys.isEmpty || self.hasNext)) + ) + def next(): A = self.synchronized { + if (this == ahead) { + val e = self.next() + xs = e :: xs; e + } else { + if (ys.isEmpty) { + ys = xs.reverse + xs = Nil + } + ys match { + case Nil => + val e = self.next() + ahead = this + xs = e :: xs; e + case z :: zs => + ys = zs; z + } + } + } + } + ahead = new Partner + (ahead, new Partner) + } + + def patch[B >: A](from: Int, ps: Sequence[B], replaced: Int) = new Iterator[B] { + private val plen = ps.length + private var origElems = self + private val patchElems = ps.elements + private var i = 0 + def hasNext: Boolean = + if (i < from) origElems.hasNext + else patchElems.hasNext || origElems.hasNext + def next(): B = { + val result: B = + if (i < from || i >= from + plen) origElems.next() + else patchElems.next() + i += 1 + if (i == from) origElems = origElems drop replaced + result + } + } + + /** Fills the given array xs with at most `len` elements of + * this iterator starting at position `start`. + * Copying will stop once either the end of the current iterable is reached or + * `len` elements have been copied. + * + * @param xs the array to fill. + * @param start starting index. + * @param len number of elements to copy + * @pre the array must be large enough to hold all elements. + */ + def copyToArray[B >: A](xs: Array[B], start: Int, len: Int): Unit = { + var i = start + val end = start + len + while (hasNext && i < end) { + xs(i) = next() + i += 1 + } + } + + /** Fills the given array xs with the elements of + * this iterator starting at position start + * until either the end of the current iterator or the end of array `xs` is reached. + * + * @param xs the array to fill. + * @param start starting index. + * @pre the array must be large enough to hold all elements. + */ + def copyToArray[B >: A](xs: Array[B], start: Int): Unit = + copyToArray(xs, start, xs.length - start) + + /** Fills the given array xs with the elements of + * this iterator starting at position 0 + * until either the end of the current iterator or the end of array `xs` is reached. + * + * @param xs the array to fill. + * @pre the array must be large enough to hold all elements. + */ + def copyToArray[B >: A](xs: Array[B]): Unit = copyToArray(xs, 0, xs.length) + + /** Fills the given array xs with the elements of + * this sequence starting at position start. Like copyToArray, + * but designed to accomodate IO stream operations. + * + * @param xs the array to fill. + * @param start the starting index. + * @param sz the maximum number of elements to be read. + * @pre the array must be large enough to hold sz elements. + * @deprecated use copyToArray instead + */ + @deprecated def readInto[B >: A](xs: Array[B], start: Int, sz: Int) { + var i = start + while (hasNext && i - start < sz) { + xs(i) = next + i += 1 + } + } + @deprecated def readInto[B >: A](xs: Array[B], start: Int) { + readInto(xs, start, xs.length - start) + } + @deprecated def readInto[B >: A](xs: Array[B]) { + readInto(xs, 0, xs.length) + } + + /** Copy all elements to a buffer + * @param The buffer to which elements are copied + */ + def copyToBuffer[B >: A](dest: Buffer[B]) { + while (hasNext) dest += next + } + + /** Transform this iterator into a list of all elements. + * + * @return a list which enumerates all elements of this iterator. + */ + def toList: List[A] = { + val res = new ListBuffer[A] + while (hasNext) res += next + res.toList + } + + /** + * Create a stream which contains all the elements of this iterator. + */ + def toStream: Stream[A] = + if (hasNext) Stream.cons(next, toStream) else Stream.empty + + /** + * Create a sequence which contains all the elements of this iterator. + */ + def toSequence: Sequence[A] = { + val buffer = new ArrayBuffer[A] + this copyToBuffer buffer + null // !!! should be: buffer.readOnly + } + + /** Collect elements into a seq. + * + * @return a sequence which enumerates all elements of this iterator. + * @deprecated use toSequence instead + */ + @deprecated def collect: Sequence[A] = toSequence + + /** Returns a string representation of the elements in this iterator. The resulting string + * begins with the string start and is finished by the string + * end. Inside, the string representations of elements (w.r.t. + * the method toString()) are separated by the string + * sep. + *

+ * Ex:
+ * List(1, 2, 3).mkString("(", "; ", ")") = "(1; 2; 3)" + * + * @param start starting string. + * @param sep separator string. + * @param end ending string. + * @return a string representation of this iterable object. + */ + def mkString(start: String, sep: String, end: String): String = { + val buf = new StringBuilder + addString(buf, start, sep, end).toString + } + + /** Returns a string representation of this iterable object. The string + * representations of elements (w.r.t. the method toString()) + * are separated by the string sep. + * + * @param sep separator string. + * @return a string representation of this iterable object. + */ + def mkString(sep: String): String = this.mkString("", sep, "") + + /** Returns a string representation of this iterable object. The string + * representations of elements (w.r.t. the method toString()) + * are separated by a comma. + * + * @return a string representation of this iterable object. + */ + def mkString: String = + mkString("") + + /** Write all elements of this iterator into given string builder. + * The written text begins with the string start and is finished by the string + * end. Inside, the string representations of elements (w.r.t. + * the method toString()) are separated by the string + * sep. + */ + def addString(buf: StringBuilder, start: String, sep: String, end: String): StringBuilder = { + buf.append(start) + val elems = this + if (elems.hasNext) buf.append(elems.next) + while (elems.hasNext) { + buf.append(sep); buf.append(elems.next) + } + buf.append(end) + } + + /** Write all elements of this iterator into given string builder. + * The string representations of elements (w.r.t. the method toString()) + * are separated by the string sep. + */ + def addString(buf: StringBuilder, sep: String): StringBuilder = + addString(buf, "", sep, "") + + /** Write all elements of this string into given string builder without using + * any separator between consecutive elements. + */ + def addString(buf: StringBuilder): StringBuilder = + addString(buf, "", "", "") + + override def toString = (if (hasNext) "non-empty" else "empty")+" iterator" + +} diff --git a/src/library/scalax/collection/OrderedIterable.scala b/src/library/scalax/collection/OrderedIterable.scala new file mode 100755 index 0000000000..97bd96e667 --- /dev/null +++ b/src/library/scalax/collection/OrderedIterable.scala @@ -0,0 +1,41 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Iterable.scala 15188 2008-05-24 15:01:02Z stepancheg $ + + +package scalax.collection + +import generic._ + +/** An ordered collection is a collection with a fixed sequence of elements + * which corresponds to append order. In particular, it holds that + * + * (c1 ++ c2).elements = c1.elements ++ c2.elements + * + * for any two ordered collections c1 and c2. + * Ordered collections support + * - operations that form subsequences: take, takeWhile, drop, dropWhile, + * - zip, unzip + * + * @author Martin Odersky + * @version 2.8 + */ +trait OrderedIterable[+A] extends Iterable[A] with covariant.OrderedIterableTemplate[OrderedIterable, A] + +/** Various utilities for instances of Iterable. + * + * @author Matthias Zenger + * @author Martin Odersky + * @version 2.8 + */ +object OrderedIterable extends covariant.IterableFactory[OrderedIterable] { + + val empty: OrderedIterable[Nothing] = null // !!! + +} diff --git a/src/library/scalax/collection/Sequence.scala b/src/library/scalax/collection/Sequence.scala new file mode 100755 index 0000000000..0ee88e3575 --- /dev/null +++ b/src/library/scalax/collection/Sequence.scala @@ -0,0 +1,45 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Sequence.scala 16092 2008-09-12 10:37:06Z nielsen $ + + +package scalax.collection + +import generic._ + +/** Class Sequence[A] represents finite sequences of elements + * of type A. + * + * @author Martin Odersky + * @author Matthias Zenger + * @version 1.0, 16/07/2003 + */ +trait Sequence[+A] extends OrderedIterable[A] with SizedIterable[A] with covariant.SequenceTemplate[Sequence, A] + +object Sequence extends covariant.SequenceFactory[Sequence] { + + /** The empty sequence */ + val empty : Sequence[Nothing] = null // !!! + + type View[+UC[+B] <: Sequence[B], +A] = covariant.SequenceView[UC, A] + + /** @deprecated use View instead + */ + @deprecated type Projection[A] = View[C, A] forSome { type C[+B] <: Sequence[B] } + + /** @deprecated use Sequence(value) instead */ + @deprecated def singleton[A](value: A) = Sequence(value) + + /** Builds a singleton sequence. + * + * @deprecated use Sequence(x) instead. + */ + @deprecated def single[A](x: A) = singleton(x) +} + diff --git a/src/library/scalax/collection/SizedIterable.scala b/src/library/scalax/collection/SizedIterable.scala new file mode 100644 index 0000000000..f4e13f3521 --- /dev/null +++ b/src/library/scalax/collection/SizedIterable.scala @@ -0,0 +1,38 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2007, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Collection.scala 12340 2007-07-17 15:29:47Z mcdirmid $ + + +package scalax.collection + +/** Variant of Iterable which also demands + * implementation of a `size` method. + * Basically, this trait just adds size to Iterable, + * and provides an optimized implementation of toArray based on it. + * + * @author Martin Odersky + * @version 2.8 + */ +trait SizedIterable[+A] extends Iterable[A] { + + /** Returns the number of elements in this collection. + * + * @return number of collection elements. + */ + def size : Int + + /** Converts this iterable to a fresh Array with size elements. + */ + override def toArray[B >: A]: Array[B] = { + val result = new Array[B](size) + copyToArray(result, 0) + result + } +} + diff --git a/src/library/scalax/collection/Vector.scala b/src/library/scalax/collection/Vector.scala new file mode 100755 index 0000000000..8787d7fd42 --- /dev/null +++ b/src/library/scalax/collection/Vector.scala @@ -0,0 +1,21 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2006-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Vector.scala 15437 2008-06-25 16:22:45Z stepancheg $ + +package scalax.collection + +import generic._ + +trait Vector[+A] extends Sequence[A] with covariant.VectorTemplate[Vector, A] + +object Vector extends covariant.SequenceFactory[Vector] { + + /** The empty sequence */ + val empty : Vector[Nothing] = null // !!! +} diff --git a/src/library/scalax/collection/generic/IterableFactory.scala b/src/library/scalax/collection/generic/IterableFactory.scala new file mode 100755 index 0000000000..264e333ac5 --- /dev/null +++ b/src/library/scalax/collection/generic/IterableFactory.scala @@ -0,0 +1,108 @@ +package scalax.collection.generic + +trait IterableFactory[CC[A] <: Iterable[A]] { + + /** Create CC collection of specified elements */ + def apply[A](args: A*): CC[A] + + protected def newBuilder[A]: Builder[CC, A] = + apply().newBuilder[A].asInstanceOf[Builder[CC, A]] + + /** Concatenate all the argument lists into a single list. + * + * @param xss the lists that are to be concatenated + * @return the concatenation of all the lists + */ + def concat[A](xss: CC[A]*): CC[A] = { + val b = newBuilder[A] + for (xs <- xss) b ++= xs + b.result + } + + /** An iterable that contains the same element a number of times + * @param n The number of elements returned + * @param elem The element returned each time + */ + def fill[A](n: Int, elem: => A): CC[A] = { + val b = newBuilder[A] + var i = 0 + while (i < n) { + b += elem + i += 1 + } + b.result + } + + def fill[A](n1: Int, n2: Int, elem: => A): CC[CC[A]] = + tabulate(n1)(_ => fill(n2, elem)) + + def fill[A](n1: Int, n2: Int, n3: Int, elem: => A): CC[CC[CC[A]]] = + tabulate(n1)(_ => fill(n2, n3, elem)) + + def tabulate[A](n: Int)(f: Int => A): CC[A] = { + val b = newBuilder[A] + var i = 0 + while (i < n) { + b += f(i) + i += 1 + } + b.result + } + + def tabulate[A](n1: Int, n2: Int)(f: (Int, Int) => A): CC[CC[A]] = + tabulate(n1)(i1 => tabulate(n2)(i2 => f(i1, i2))) + + def tabulate[A](n1: Int, n2: Int, n3: Int)(f: (Int, Int, Int) => A): CC[CC[CC[A]]] = + tabulate(n1)(i1 => tabulate(n2)(i2 => tabulate(n3)(i3 => f(i1, i2, i3)))) +// todo: go up to 5(?) + + /** Create a sequence of increasing integers in a range. + * + * @param from the start value of the sequence + * @param end the end value of the sequence + * @return the sorted list of all from `from` (inclusive) + * up to, but exclusding, `end`. + */ + def range[A](start: Int, end: Int): CC[Int] = range(start, end, 1) + + /** Create a sequence of increasing integers in a range. + * + * @param from the start value of the sequence + * @param end the end value of the sequence + * @param step the increment value of successive elements + * @return a list of values from + n * step for + * increasing n. If `step > 0` the sequence terminates + * with the largest value less than `end`. If `step < 0` + * the sequence terminates with the smallest value greater than `end`. + * If `step == 0`, the sequence gors on infinitely (in that + * case the `range` operation might not terminate. + */ + def range(start: Int, end: Int, step: Int): CC[Int] = { + val b = newBuilder[Int] + var i = start + while ((step <= 0 || i < end) && (step >= 0 || i > end)) { + b += i + i += step + } + b.result + } + + /** Create a sequence by repeatedly applying a given function to a start value. + * + * @param start the start value of the sequence + * @param len the length of the sequence + * @param f the function that's repeatedly applied + * @return the sequence with elements (start, f(start), f(f(start)), ..., flen-1(start)) + */ + def iterate(start: Int, len: Int)(f: Int => Int): CC[Int] = { + val b = newBuilder[Int] + var acc = start + var i = 0 + while (i < len) { + b += acc + acc = f(acc) + i += 1 + } + b.result + } +} diff --git a/src/library/scalax/collection/generic/IterableForwarder.scala b/src/library/scalax/collection/generic/IterableForwarder.scala new file mode 100644 index 0000000000..fba1e6d0e9 --- /dev/null +++ b/src/library/scalax/collection/generic/IterableForwarder.scala @@ -0,0 +1,61 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: IterableProxy.scala 15458 2008-06-28 20:23:22Z stepancheg $ + + +package scalax.collection.generic + +import collection.mutable.Buffer +import collection.immutable.List + +/** This trait implements a forwarder for iterable objects. It forwards + * all calls to a different iterable object, except for + * + * - toString, hashCode, equals, stringPrefix + * - newBuilder, view + * - all calls creating a new iterable objetc of the same kind + * + * The above methods are forwarded by subclass IterableProxy + * + * @author Martin Odersky + * @version 2.8 + */ +trait IterableForwarder[+A] extends Iterable[A] { + + /** The iterable object to which calls are forwarded */ + protected def underlying: Iterable[A] + + // Iterable delegates + // Iterable methods could be printed by cat IterableTemplate.scala | sed -n '/trait Iterable/,$ p' | egrep '^ (override )?def' + + override def elements = underlying.elements + override def isEmpty = underlying.isEmpty + override def hasDefiniteSize = underlying.hasDefiniteSize + override def foreach(f: A => Unit) = underlying.foreach(f) + override def forall(p: A => Boolean): Boolean = underlying.forall(p) + override def exists(p: A => Boolean): Boolean = underlying.exists(p) + override def count(p: A => Boolean): Int = underlying.count(p) + override def find(p: A => Boolean): Option[A] = underlying.find(p) + override def foldLeft[B](z: B)(op: (B, A) => B): B = underlying.foldLeft(z)(op) + override def foldRight[B](z: B)(op: (A, B) => B): B = underlying.foldRight(z)(op) + override def reduceLeft[B >: A](op: (B, A) => B): B = underlying.reduceLeft(op) + override def reduceRight[B >: A](op: (A, B) => B): B = underlying.reduceRight(op) + override def copyToBuffer[B >: A](dest: Buffer[B]) = underlying.copyToBuffer(dest) + override def copyToArray[B >: A](xs: Array[B], start: Int, len: Int) = underlying.copyToArray(xs, start, len) + override def toArray[B >: A]: Array[B] = underlying.toArray + override def toList: List[A] = underlying.toList + override def toSequence: Sequence[A] = underlying.toSequence + override def toStream: Stream[A] = underlying.toStream + override def mkString(start: String, sep: String, end: String): String = underlying.mkString(start, sep, end) + override def addString(b: StringBuilder, start: String, sep: String, end: String): StringBuilder = underlying.addString(b, start, sep, end) + + override def head: A = underlying.head + override def last: A = underlying.last + override def sameElements[B >: A](that: OrderedIterable[B]): Boolean = underlying.sameElements(that) +} diff --git a/src/library/scalax/collection/generic/IterableTemplate.scala b/src/library/scalax/collection/generic/IterableTemplate.scala new file mode 100755 index 0000000000..01a23f5ecf --- /dev/null +++ b/src/library/scalax/collection/generic/IterableTemplate.scala @@ -0,0 +1,816 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Iterable.scala 15188 2008-05-24 15:01:02Z stepancheg $ + + +package scalax.collection.generic + +import scalax.collection.mutable.{Buffer, ArrayBuffer, ListBuffer} +import scalax.collection.immutable.{List, Nil, ::} +import util.control.Break._ +import Iterable._ + +/** Collection classes mixing in this class provide a method + * elements which returns an iterator over all the + * elements contained in the collection. + * + * @note If a collection has a known size, it should also sub-type Collection. + * Only potentially unbounded collections should directly sub-class Iterable. + * @author Matthias Zenger + * @author Martin Odersky + * @version 2.8 + */ +trait IterableTemplate[+CC[/*+*/B] <: IterableTemplate[CC, B] with Iterable[B], /*+*/A] { self/*: CC[A]*/ => + + /** The template itself seen as an instance of `CC[A]`. + * @note: It would be logical to have a self type `CC[A]` instead, then we would not need + * this method. Unfortunately, tyis runs afoul some pecularities in Scala's member resolution + * algorithm: If the self type is a CC, then Iterable is one of its supertypes. Iterable + * defines a number of concrete methods such as newBuilder which are abstract here. + * The newBuilder method in Iterable[A] has type Builder[Iterable, A]. Because Scala + * prefers concrete over abstract members, it is this newBuilder which is chosen, instead of + * the abstract newBuilder in class IterableTemplate of type Builder[CC, A]. + * Even for concrete methods we have a problem because the last mixin in the parents of CC is + * Iterable, not IterableTemplate. So resolution picks the version in Iterable, which returns + * again an Iterable, not a CC, as would be required. + * These problems would be avoided if Scala computed the type of a member as the glb of the types + * all members in the class and its superclasses types. + * I think overall this would be a better design. + */ + protected[this] def thisCC: CC[A] = this.asInstanceOf[CC[A]] + + /** Creates a new iterator over all elements contained in this + * object. + * + * @return the new iterator + */ + def elements: Iterator[A] + + /** Create a new builder for this IterableType + */ + def newBuilder[B]: Builder[CC, B] + + /** Is this collection empty? */ + def isEmpty: Boolean = !elements.hasNext + + /** returns true iff this collection has a bound size. + * Many APIs in this trait will not work on collections of + * unbound sizes. + */ + def hasDefiniteSize = true + + /** Create a new sequence of type CC which contains all elements of this sequence + * followed by all elements of Iterable `that' + */ + def ++[B >: A](that: Iterable[B]): CC[B] = { + val b: Builder[CC, B] = (this: IterableTemplate[CC, A]).newBuilder[B] + b ++= thisCC + b ++= that + b.result + } + + /** Create a new sequence of type IterableType which contains all elements of this sequence + * followed by all elements of Iterator `that' + */ + def ++[B >: A](that: Iterator[B]): CC[B] = { + val b = newBuilder[B] + b ++= thisCC + b ++= that + b.result + } + + /** Returns the sequence resulting from applying the given function + * f to each element of this sequence. + * + * @param f function to apply to each element. + * @return f(a0), ..., f(an) if this + * sequence is a0, ..., an. + */ + def map[B](f: A => B): CC[B] = { + val b = newBuilder[B] + for (x <- this) b += f(x) + b.result + } + + /** Applies the given function f to each element of + * this sequence, then concatenates the results. + * + * @param f the function to apply on each element. + * @return f(a0) ::: ... ::: f(an) if + * this sequence is a0, ..., an. + */ + def flatMap[B](f: A => Iterable[B]): CC[B] = { + val b = newBuilder[B] + for (x <- this) b ++= f(x) + b.result + } + + /** Returns all the elements of this sequence that satisfy the + * predicate p. The order of the elements is preserved. + * @param p the predicate used to filter the list. + * @return the elements of this list satisfying p. + */ + def filter(p: A => Boolean): CC[A] = { + val b = newBuilder[A] + for (x <- this) + if (p(x)) b += x + b.result + } + + /** Removes all elements of the iterable which satisfy the predicate + * p. This is like filter with the + * predicate inversed. + * + * @param p the predicate to use to test elements + * @return the list without all elements which satisfy p + */ + def remove(p: A => Boolean): CC[A] = filter(!p(_)) + + /** Partitions this iterable in two iterables according to a predicate. + * + * @param p the predicate on which to partition + * @return a pair of iterables: the iterable that satisfies the predicate + * p and the iterable that does not. + * The relative order of the elements in the resulting iterables + * is the same as in the original iterable. + */ + def partition(p: A => Boolean): (CC[A], CC[A]) = { + val l, r = newBuilder[A] + for (x <- this) (if (p(x)) l else r) += x + (l.result, r.result) + } + + /** Apply a function f to all elements of this + * iterable object. + * + * @note Will not terminate for infinite-sized collections. + * @param f a function that is applied to every element. + * Note this function underlies the implementation of most other bulk operations. + * It should be overridden in concrete collectionc classes with efficient implementations. + */ + def foreach(f: A => Unit): Unit = elements.foreach(f) + + /** Return true iff the given predicate `p` yields true for all elements + * of this iterable. + * + * @note May not terminate for infinite-sized collections. + * @param p the predicate + */ + def forall(p: A => Boolean): Boolean = { + var result = true + breakable { + for (x <- this) + if (!p(x)) { result = false; break } + } + result + } + + /** Return true iff there is an element in this iterable for which the + * given predicate `p` yields true. + * + * @note May not terminate for infinite-sized collections. + * @param p the predicate + */ + def exists(p: A => Boolean): Boolean = { + var result = false + breakable { + for (x <- this) + if (p(x)) { result = true; break } + } + result + } + + /** Count the number of elements in the iterable which satisfy a predicate. + * + * @param p the predicate for which to count + * @return the number of elements satisfying the predicate p. + */ + def count(p: A => Boolean): Int = { + var cnt = 0 + for (x <- this) { + if (p(x)) cnt += 1 + } + cnt + } + + /** Find and return the first element of the iterable object satisfying a + * predicate, if any. + * + * @note may not terminate for infinite-sized collections. + * @param p the predicate + * @return an option containing the first element in the iterable object + * satisfying p, or None if none exists. + */ + def find(p: A => Boolean): Option[A] = { + var result: Option[A] = None + breakable { + for (x <- this) + if (p(x)) { result = Some(x); break } + } + result + } + + /** Combines the elements of this iterable object together using the binary + * function f, from left to right, and starting with + * the value z. + * + * @note Will not terminate for infinite-sized collections. + * @return f(... (f(f(z, a0), a1) ...), + * an) if the list is + * [a0, a1, ..., an]. + */ + def foldLeft[B](z: B)(op: (B, A) => B): B = { + var result = z + for (x <- this) + result = op(result, x) + result + } + + /** Combines the elements of this list together using the binary + * function f, from right to left, and starting with + * the value z. + * + * @note Will not terminate for infinite-sized collections. + * @return f(a0, f(a1, f(..., f(an, z)...))) + * if the list is [a0, a1, ..., an]. + */ + def foldRight[B](z: B)(op: (A, B) => B): B = elements.foldRight(z)(op) + + /** Similar to foldLeft but can be used as + * an operator with the order of list and zero arguments reversed. + * That is, z /: xs is the same as xs foldLeft z + * @note Will not terminate for infinite-sized collections. + */ + def /: [B](z: B)(op: (B, A) => B): B = foldLeft(z)(op) + + /** An alias for foldRight. + * That is, xs :\ z is the same as xs foldRight z + * @note Will not terminate for infinite-sized collections. + */ + def :\ [B](z: B)(op: (A, B) => B): B = foldRight(z)(op) + + /** Combines the elements of this iterable object together using the binary + * operator op, from left to right + * @note Will not terminate for infinite-sized collections. + * @param op The operator to apply + * @return op(... op(a0,a1), ..., an) + if the iterable object has elements + * a0, a1, ..., an. + * @throws Predef.UnsupportedOperationException if the iterable object is empty. + */ + def reduceLeft[B >: A](op: (B, A) => B): B = { + if (isEmpty) throw new UnsupportedOperationException("empty.reduceLeft") + var result: B = elements.next + var first = true + for (x <- this) + if (first) first = false + else result = op(result, x) + result + } + + /** Combines the elements of this iterable object together using the binary + * operator op, from right to left + * @note Will not terminate for infinite-sized collections. + * @param op The operator to apply + * + * @return a0 op (... op (an-1 op an)...) + * if the iterable object has elements a0, a1, ..., + * an. + * + * @throws Predef.UnsupportedOperationException if the iterator is empty. + */ + def reduceRight[B >: A](op: (A, B) => B): B = + elements.reduceRight(op) + + /** Returns an iterable formed from this iterable and the specified list + * `other` by associating each element of the former with + * the element at the same position in the latter. + * If one of the two iterables is longer than the other, its remaining elements are ignored. + */ + def zip[B](that: Iterable[B]): CC[(A, B)] = { + val these = this.elements + val those = that.elements + val b = this.newBuilder[(A, B)] + while (these.hasNext && those.hasNext) + b += ((these.next, those.next)) + b.result + } + + /** Returns a iterable formed from this iterable and the specified iterable + * that by associating each element of the former with + * the element at the same position in the latter. + * + * @param that iterable that may have a different length + * as the self iterable. + * @param thisElem element thisElem is used to fill up the + * resulting iterable if the self iterable is shorter than + * that +b * @param thatElem element thatElem is used to fill up the + * resulting iterable if that is shorter than + * the self iterable + * @return Iterable((a0,b0), ..., + * (an,bn), (elem,bn+1), + * ..., {elem,bm}) + * when [a0, ..., an] zip + * [b0, ..., bm] is + * invoked where m > n. + */ + def zipAll[B, A1 >: A, B1 >: B](that: Iterable[B], thisElem: A1, thatElem: B1): CC[(A1, B1)] = { + val these = this.elements + val those = that.elements + val b = newBuilder[(A1, B1)] + while (these.hasNext && those.hasNext) + b += ((these.next, those.next)) + while (these.hasNext) + b += ((these.next, thatElem)) + while (those.hasNext) + b += ((thisElem, those.next)) + b.result + } + + /** Zips this iterable with its indices. `s.zipWithIndex` is equivalent to + * `s zip s.indices`, but is usually more efficient. + */ + def zipWithIndex: CC[(A, Int)] = { + val b = newBuilder[(A, Int)] + var i = 0 + for (x <- this) { + b += ((x, i)) + i +=1 + } + b.result + } + + /** Copy all elements to a given buffer + * @note Will not terminate for infinite-sized collections. + * @param dest The buffer to which elements are copied + */ + def copyToBuffer[B >: A](dest: Buffer[B]) { + for (x <- this) dest += x + } + + /** Fills the given array xs with at most `len` elements of + * this sequence starting at position `start`. + * Copying will stop oce either the end of the current iterable is reached or + * `len` elements have been copied. + * + * @note Will not terminate for infinite-sized collections. + * @param xs the array to fill. + * @param start starting index. + * @param len number of elements to copy + * @pre the array must be large enough to hold all elements. + */ + def copyToArray[B >: A](xs: Array[B], start: Int, len: Int) { + var i = start + val end = (start + len) min xs.length + for (x <- this) { + if (i < end) { + xs(i) = x + i += 1 + } + } + } + + /** Fills the given array xs with the elements of + * this sequence starting at position start + * until either the end of the current iterable or the end of array `xs` is reached. + * + * @note Will not terminate for infinite-sized collections. + * @param xs the array to fill. + * @param start starting index. + * @pre the array must be large enough to hold all elements. + */ + def copyToArray[B >: A](xs: Array[B], start: Int) { + copyToArray(xs, start, xs.length - start) + } + + /** Converts this collection to a fresh Array elements. + * @note Will not terminate for infinite-sized collections. + */ + def toArray[B >: A]: Array[B] = { + var size = 0 + for (x <- this) size += 1 + val result = new Array[B](size) + copyToArray(result, 0) + result + } + + /** + * Create a fresh list with all the elements of this iterable object. + * @note Will not terminate for infinite-sized collections. + */ + def toList: List[A] = (new ListBuffer[A] ++ thisCC).toList + + /** + * Returns a sequence containing all of the elements in this iterable object. + * @note Will not terminate for infinite-sized collections. + */ + def toSequence: Sequence[A] = toList.asInstanceOf[Sequence[A]] // !!! + + /** @deprecated use toSequence instead + */ + @deprecated def toSeq: Sequence[A] = toSequence + + /** + * Create a stream which contains all the elements of this iterable object. + * @note consider using projection for lazy behavior. + */ + def toStream: Stream[A] = elements.toStream + + /** Sort the iterable according to the comparison function + * <(e1: a, e2: a) => Boolean, + * which should be true iff e1 is smaller than + * e2. + * The sort is stable. That is elements that are equal wrt `lt` appear in the + * same order in the sorted iterable as in the original. + * + * @param lt the comparison function + * @return a list sorted according to the comparison function + * <(e1: a, e2: a) => Boolean. + * @ex

+   *    List("Steve", "Tom", "John", "Bob")
+   *      .sort((e1, e2) => (e1 compareTo e2) < 0) =
+   *    List("Bob", "John", "Steve", "Tom")
+ * !!! + def sortWith(lt : (A,A) => Boolean): CC[A] = { + val arr = toArray + Array.sortWith(arr, lt) + val b = newBuilder[A] + for (x <- arr) b += x + b.result + } + */ + + /** Returns a string representation of this iterable object. The resulting string + * begins with the string start and is finished by the string + * end. Inside, the string representations of elements (w.r.t. + * the method toString()) are separated by the string + * sep. + * + * @ex List(1, 2, 3).mkString("(", "; ", ")") = "(1; 2; 3)" + * @note Will not terminate for infinite-sized collections. + * @param start starting string. + * @param sep separator string. + * @param end ending string. + * @return a string representation of this iterable object. + */ + def mkString(start: String, sep: String, end: String): String = + addString(new StringBuilder(), start, sep, end).toString + + /** Returns a string representation of this iterable object. The string + * representations of elements (w.r.t. the method toString()) + * are separated by the string sep. + * + * @note Will not terminate for infinite-sized collections. + * @param sep separator string. + * @return a string representation of this iterable object. + */ + def mkString(sep: String): String = + addString(new StringBuilder(), sep).toString + + /** Converts a collection into a flat String by each element's toString method. + * @note Will not terminate for infinite-sized collections. + */ + def mkString = + addString(new StringBuilder()).toString + + /** Write all elements of this iterable into given string builder. + * The written text begins with the string start and is finished by the string + * end. Inside, the string representations of elements (w.r.t. + * the method toString()) are separated by the string + * sep. + * @note Will not terminate for infinite-sized collections. + */ + def addString(b: StringBuilder, start: String, sep: String, end: String): StringBuilder = { + b append start + var first = true + for (x <- this) { + if (first) first = false + else b append sep + b append x + } + b append end + } + + /** Write all elements of this string into given string builder. + * The string representations of elements (w.r.t. the method toString()) + * are separated by the string sep. + * @note Will not terminate for infinite-sized collections. + */ + def addString(b: StringBuilder, sep: String): StringBuilder = { + var first = true + for (x <- this) { + if (first) first = false + else b append sep + b append x + } + b + } + + /** Write all elements of this string into given string builder without using + * any separator between consecutive elements. + * @note Will not terminate for infinite-sized collections. + */ + def addString(b: StringBuilder): StringBuilder = { + for (x <- this) { + b append x + } + b + } + + /** + * returns a projection that can be used to call non-strict filter, + * map, and flatMap methods that build projections + * of the collection. + def projection : Iterable.Projection[A] = new Iterable.Projection[A] { + def elements = Iterable.this.elements + override def force = Iterable.this + } + */ + + override def toString = mkString(stringPrefix + "(", ", ", ")") + + /** Defines the prefix of this object's toString representation. + */ + def stringPrefix : String = { + var string = this.getClass.getName + val idx1 = string.lastIndexOf('.' : Int) + if (idx1 != -1) string = string.substring(idx1 + 1) + val idx2 = string.indexOf('$') + if (idx2 != -1) string = string.substring(0, idx2) + string + } + + + /** Creates a view of this iterable @see IterableView + */ + def view: IterableView[CC, A] = new IterableView[CC, A] { // !!! Martin: We should maybe infer the type parameters here? + val origin = thisCC + val elements: Iterator[A] = self.elements + } + +// The following methods return non-deterministic results, unless this iterable is an OrderedIterable + + /** The first element of this sequence. + * + * @note Might return different results for different runs, unless this iterable is ordered + * @throws Predef.NoSuchAentException if the sequence is empty. + */ + def head: A = if (isEmpty) throw new NoSuchElementException else elements.next + + /** @deprecated use head instead */ + @deprecated def first: A = head + + /** Returns as an option the first element of this iterable + * or None if iterable is empty. + * @note Might return different results for different runs, unless this iterable is ordered + */ + def headOption: Option[A] = if (isEmpty) None else Some(head) + + /** @deprecated use headOption instead + * None if list is empty. + */ + @deprecated def firstOption: Option[A] = headOption + + /** An iterable consisting of all elements of this iterable + * except the first one. + * @note Might return different results for different runs, unless this iterable is ordered + */ + def tail: CC[A] = drop(1) + + /** Return an iterable consisting only of the first n + * elements of this iterable, or else the whole iterable, if it has less + * than n elements. + * + * @param n the number of elements to take + * @return a possibly projected sequence + * @note Might return different results for different runs, unless this iterable is ordered + */ + def take(n: Int): CC[A] = { + val b = newBuilder[A] + var i = 0 + breakable { + for (x <- this) { + b += x + i += 1 + if (i == n) break + } + } + b.result + } + + /** Returns this iterable without its n first elements + * If this iterable has less than n elements, the empty + * iterable is returned. + * + * @param n the number of elements to drop + * @return the new iterable + * @note Might return different results for different runs, unless this iterable is ordered + */ + def drop(n: Int): CC[A] = { + val b = newBuilder[A] + var i = 0 + for (x <- this) { + if (i >= n) b += x + i += 1 + } + b.result + } + + /** A sub-iterable starting at index `from` + * and extending up to (but not including) index `until`. + * + * @note c.slice(from, to) is equivalent to (but possibly more efficient than) + * c.drop(from).take(to - from) + * + * @param from The index of the first element of the returned subsequence + * @param until The index of the element following the returned subsequence + * @throws IndexOutOfBoundsException if from < 0 + * or length < from + len + * @note Might return different results for different runs, unless this iterable is ordered + */ + def slice(from: Int, until: Int): CC[A] = { + val b = newBuilder[A] + var i = 0 + breakable { + for (x <- this) { + if (i >= from) b += x + i += 1 + if (i == until) break + } + } + b.result + } + + /** The last element of this iterable. + * + * @throws Predef.NoSuchElementException if the sequence is empty. + * @note Might return different results for different runs, unless this iterable is ordered + */ + def last: A = { + var lst = head + for (x <- this) + lst = x + lst + } + + /** Returns as an option the last element of this iterable or + * None if iterable is empty. + * + * @return the last element as an option. + * @note Might return different results for different runs, unless this iterable is ordered + */ + def lastOption: Option[A] = if (isEmpty) None else Some(last) + + /** An iterable consisting of all elements of this iterable except the last one. + * @note Might return different results for different runs, unless this iterable is ordered + */ + def init: CC[A] = { + var lst = head + val b = newBuilder[A] + for (x <- this) { + b += lst + lst = x + } + b.result + } + + /** Returns the rightmost n elements from this iterable. + * + * @param n the number of elements to take + * @note Might return different results for different runs, unless this iterable is ordered + */ + def takeRight(n: Int): CC[A] = { + val b = newBuilder[A] + val lead = elements drop n + var go = false + for (x <- this) { + if (go) b += x + else if (lead.hasNext) lead.next + else go = true + } + b.result + } + + /** Returns the iterable wihtout its rightmost n elements. + * + * @param n the number of elements to take + * @note Might return different results for different runs, unless this iterable is ordered + */ + def dropRight(n: Int): CC[A] = { + val b = newBuilder[A] + val lead = elements drop n + breakable { + for (x <- this) { + if (!lead.hasNext) break + lead.next + b += x + } + } + b.result + } + + /** Split the iterable at a given point and return the two parts thus + * created. + * + * @param n the position at which to split + * @return a pair of iterables composed of the first n + * elements, and the other elements. + * @note Might return different results for different runs, unless this iterable is ordered + */ + def splitAt(n: Int): (CC[A], CC[A]) = { + val l, r = newBuilder[A] + var i = 0 + for (x <- this) + (if (i < n) l else r) += x + (l.result, r.result) + } + + /** Returns the longest prefix of this sequence whose elements satisfy + * the predicate p. + * + * @param p the test predicate. + * @return the longest prefix of this sequence whose elements satisfy + * the predicate p. + * @note Might return different results for different runs, unless this iterable is ordered + */ + def takeWhile(p: A => Boolean): CC[A] = { + val b = newBuilder[A] + breakable { + for (x <- this) { + if (!p(x)) break + b += x + } + } + b.result + } + + /** Returns the longest suffix of this sequence whose first element + * does not satisfy the predicate p. + * + * @param p the test predicate. + * @return the longest suffix of the sequence whose first element + * does not satisfy the predicate p. + * @note Might return different results for different runs, unless this iterable is ordered + */ + def dropWhile(p: A => Boolean): CC[A] = { + val b = newBuilder[A] + var go = false + for (x <- this) { + if (go) b += x + else if (!p(x)) { go = true; b += x } + } + b.result + } + + /** Returns a pair consisting of the longest prefix of the list whose + * elements all satisfy the given predicate, and the rest of the list. + * + * @param p the test predicate + * @return a pair consisting of the longest prefix of the list whose + * elements all satisfy p, and the rest of the list. + * @note Might return different results for different runs, unless this iterable is ordered + */ + def span(p: A => Boolean): (CC[A], CC[A]) = { + val l, r = newBuilder[A] + var toLeft = true + for (x <- this) { + toLeft = toLeft && p(x) + (if (toLeft) l else r) += x + } + (l.result, r.result) + } + + /** Checks if the other iterable object contains the same elements as this one. + * + * @note will not terminate for infinite-sized iterables. + * @param that the other iterable + * @return true, iff both iterables contain the same elements. + * @note Might return different results for different runs, unless this iterable is ordered + */ + def sameElements[B >: A](that: OrderedIterable[B]): Boolean = { + val these = this.elements + val those = that.elements + while (these.hasNext && those.hasNext && these.next() == those.next()) {} + !these.hasNext && !those.hasNext + } + + /** A sub-sequence view starting at index `from` + * and extending up to (but not including) index `until`. + * + * @param from The index of the first element of the slice + * @param until The index of the element following the slice + * @note The difference between `view` and `slice` is that `view` produces + * a view of the current sequence, whereas `slice` produces a new sequence. + * + * @note Might return different results for different runs, unless this iterable is ordered + * @note view(from, to) is equivalent to view.slice(from, to) + */ + def view(from: Int, until: Int): IterableView[CC, A] = view.slice(from, until) +} diff --git a/src/library/scalax/collection/generic/IterableView.scala b/src/library/scalax/collection/generic/IterableView.scala new file mode 100755 index 0000000000..b9b826b0a4 --- /dev/null +++ b/src/library/scalax/collection/generic/IterableView.scala @@ -0,0 +1,121 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Iterable.scala 15188 2008-05-24 15:01:02Z stepancheg $ + +package scalax.collection.generic + +/** A non-strict projection of an iterable. + * @author Sean McDirmid + * @author Martin Odersky + * @note this should really be a virtual class of SequenceFactory + */ +trait IterableView[+UC[/*+*/B] <: Iterable[B], /*+*/A] extends Iterable[A] { self => + + val origin: Iterable[_] + def elements: Iterator[A] + + val underlying: Iterable[_] = origin match { + case v: IterableView[t, _] => v.underlying + case _ => origin + } + + private def isDelay = elements eq underlying.elements + + private[this] var forced: UC[A] = _ + private[this] var wasForced = false + + def force: UC[A] = { + if (!wasForced) { + forced = { + val b = underlying.newBuilder[A] + for (x <- elements) + b += x + b.result + }.asInstanceOf[UC[A]] + wasForced = true + } + forced + } + + def newBuilder[A] = underlying.newBuilder[A] + + /** Builds a new view object. This method needs to be overridden in subclasses + * which refine in IterableView type + */ + protected def newView[B](elems: Iterator[B]) = new IterableView[UC, B] { + val origin = if (self.isDelay) self.origin else self + val elements = elems + } + + /** Non-strict variant of @see IterableLike.++ */ + override def ++[B >: A](that: Iterator[B]): IterableView[UC, B] = newView(elements ++ that) + + /** Non-strict variant of @see IterableLike.++ */ + override def ++[B >: A](that: Iterable[B]): IterableView[UC, B] = newView(elements ++ that.elements) + + /** Non-strict variant of @see IterableLike.map */ + override def map[B](f: A => B): IterableView[UC, B] = newView(elements map f) + + /** Non-strict variant of @see IterableLike.flatMap */ + override def flatMap[B](f: A => Iterable[B]): IterableView[UC, B] = newView(elements flatMap (f(_).elements)) + + /** Non-strict variant of @see IterableLike.filter */ + override def filter(p: A => Boolean): IterableView[UC, A] = newView(elements filter p) + + /** Non-strict variant of @see IterableLike.partition */ + override def partition(p: A => Boolean): (IterableView[UC, A], IterableView[UC, A]) = { + val (li, ri) = elements partition p + (newView(li), newView(ri)) + } + + /** Non-strict variant of @see IterableLike.zip */ + override def zip[B](other: Iterable[B]): IterableView[UC, (A, B)] = newView(elements zip other.elements) + + /** Non-strict variant of @see IterableLike.zipWithIndex */ + override def zipWithIndex: IterableView[UC, (A, Int)] = newView(elements.zipWithIndex) + + /* Non-strict variant of @see IterableLike.zipAll + * This is not enabled because it can't be specialized in VectorView: + * VectorView is not covariant, yet must maintain updatability. Impossible to do this + * with this type signature. + override def zipAll[B, A1 >: A, B1 >: B](that: Iterable[B], thisElem: A1, thatElem: B1): IterableView[UC, (A1, B1)] = + newView(elements zipAll (that.elements, thisElem, thatElem)) + */ + + /** Non-strict variant of @see Iterable.take */ + override def take(n: Int): IterableView[UC, A] = newView(elements take n) + + /** Non-strict variant of @see Iterable.drop */ + override def drop(n: Int): IterableView[UC, A] = newView(elements drop n) + + /** Non-strict variant of @see Iterable.splitAt */ + override def splitAt(n: Int): (IterableView[UC, A], IterableView[UC, A]) = (take(n), drop(n)) + + /** Non-strict variant of @see Iterable.slice */ + override def slice(from: Int, until: Int): IterableView[UC, A] = newView(elements slice (from, until)) + + /** Non-strict variant of @see Iterable.takeWhile */ + override def takeWhile(p: A => Boolean): IterableView[UC, A] = newView(elements takeWhile p) + + /** Non-strict variant of @see Iterable.dropWhile */ + override def dropWhile(p: A => Boolean): IterableView[UC, A] = newView(elements dropWhile p) + + /** Non-strict variant of @see Iterable.span */ + override def span(p: A => Boolean): (IterableView[UC, A], IterableView[UC, A]) = (takeWhile(p), dropWhile(p)) + + /** The projection resulting from the concatenation of this projection with the rest projection. + * @param rest The projection that gets appended to this projection + * @deprecated Use ++ instead + */ + @deprecated def append[B >: A](rest : => Iterable[B]): IterableView[UC, B] = this ++ rest.elements + + override def stringPrefix = origin.stringPrefix+"V" + + +} diff --git a/src/library/scalax/collection/generic/OrderedIterableTemplate.scala b/src/library/scalax/collection/generic/OrderedIterableTemplate.scala new file mode 100755 index 0000000000..fd2dbac8e4 --- /dev/null +++ b/src/library/scalax/collection/generic/OrderedIterableTemplate.scala @@ -0,0 +1,23 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Iterable.scala 15188 2008-05-24 15:01:02Z stepancheg $ + + +package scalax.collection.generic + +import OrderedIterable._ + +/** Ordered iterables are iterables where the `elements` method always returns elements in the same + * order (namely the order in which elements were appended to the iterable). In particular, one has + * for every two ordered iterables `xs` and `ys`: + * + * `(xs ++ ys).elements = xs.elements ++ ys.elements + */ +trait OrderedIterableTemplate[+CC[/*+*/B] <: OrderedIterableTemplate[CC, B] with OrderedIterable[B], /*+*/A] + extends IterableTemplate[CC, A] diff --git a/src/library/scalax/collection/generic/SequenceFactory.scala b/src/library/scalax/collection/generic/SequenceFactory.scala new file mode 100755 index 0000000000..ee97c80c75 --- /dev/null +++ b/src/library/scalax/collection/generic/SequenceFactory.scala @@ -0,0 +1,11 @@ +package scalax.collection.generic + +trait SequenceFactory[CC[A] <: Sequence[A]] extends IterableFactory[CC] { + + /** This method is called in a pattern match { case Sequence(...) => }. + * + * @param x the selector value + * @return sequence wrapped in an option, if this is a Sequence, otherwise none + */ + def unapplySequence[A](x: CC[A]): Some[CC[A]] = Some(x) +} diff --git a/src/library/scalax/collection/generic/SequenceForwarder.scala b/src/library/scalax/collection/generic/SequenceForwarder.scala new file mode 100644 index 0000000000..208d0079f1 --- /dev/null +++ b/src/library/scalax/collection/generic/SequenceForwarder.scala @@ -0,0 +1,50 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2007, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: SeqProxy.scala 15458 2008-06-28 20:23:22Z stepancheg $ + + +package scalax.collection.generic + +/** This class implements a forwarder for sequences. It forwards + * all calls to a different sequence object except for + * + * - toString, hashCode, equals, stringPrefix + * - newBuilder, view, toSequence + * - all calls creating a new iterable objetc of the same kind + * + * The above methods are forwarded by subclass SequenceProxy + * + * @author Martin Odersky + * @version 2.8 + */ +trait SequenceForwarder[+A] extends Sequence[A] with IterableForwarder[A] { + + protected override def underlying: Sequence[A] + + // PartialFunction delegates + + override def apply(i: Int): A = underlying.apply(i) + override def isDefinedAt(x: Int): Boolean = underlying.isDefinedAt(x) + + // Sequence delegates + // Sequence methods could be printed by cat SequenceTemplate.scala | sed -n '/trait Seq/,$ p' | egrep '^ (override )?def' + + override def length: Int = underlying.length + override def lengthCompare(l: Int) = underlying lengthCompare l + override def segmentLength(p: A => Boolean, from: Int): Int = underlying.segmentLength(p, from) + override def prefixLength(p: A => Boolean) = underlying.prefixLength(p) + override def indexWhere(p: A => Boolean, from: Int): Int = underlying.indexWhere(p, from) + override def indexOf[B >: A](elem: B, from: Int): Int = underlying.indexOf(elem, from) + override def reversedElements: Iterator[A] = underlying.reversedElements + override def startsWith[B](that: Sequence[B], offset: Int): Boolean = underlying.startsWith(that, offset) + override def endsWith[B](that: Sequence[B]): Boolean = underlying.endsWith(that) + override def indexOf[B >: A](that: Sequence[B]): Int = underlying.indexOf(that) + override def contains(elem: Any): Boolean = underlying.contains(elem) + override def indices: Range = underlying.indices +} diff --git a/src/library/scalax/collection/generic/SequenceTemplate.scala b/src/library/scalax/collection/generic/SequenceTemplate.scala new file mode 100755 index 0000000000..eba0e4bf98 --- /dev/null +++ b/src/library/scalax/collection/generic/SequenceTemplate.scala @@ -0,0 +1,382 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Sequence.scala 16092 2008-09-12 10:37:06Z nielsen $ + + +package scalax.collection.generic + +import util.control.Break._ +import scalax.collection.immutable.{List, Nil, ::} + +import Sequence._ + +trait SequenceTemplate[+CC[/*+*/B] <: SequenceTemplate[CC, B] with Sequence[B], /*+*/A] extends PartialFunction[Int, A] with OrderedIterableTemplate[CC, A] { +self /*: CC[A]*/ => + + /** Returns the length of the sequence. + * + * @return the sequence length. + */ + def length: Int + + /** Result of comparing length with operand len. + * returns x where + * x < 0 iff this.length < len + * x == 0 iff this.length == len + * x > 0 iff this.length > len. + * + * The method as implemented here does not call length directly; its running time + * is O(length min len) instead of O(length). The method should be overwritten + * if computing length is cheap. + */ + def lengthCompare(len: Int): Int = { + var i = 0 + breakable { + for (_ <- this) { + i += 1 + if (i > len) break + } + } + i - len + } + + /** Should always be length */ + def size = length + + /** Is this partial function defined for the index x? + */ + def isDefinedAt(x: Int): Boolean = (x >= 0) && (x < length) + + /** Returns index of the first element satisying a predicate, or -1, if none exists. + * + * @note may not terminate for infinite-sized collections. + * @param p the predicate + */ + def indexWhere(p: A => Boolean): Int = indexWhere(p, 0) + + /** Returns length of longest segment starting from a start index `from` + * such that every element of the segment satisfies predicate `p`. + * @note may not terminate for infinite-sized collections. + * @param p the predicate + * @param from the start index + */ + def segmentLength(p: A => Boolean, from: Int): Int = { + var result = 0 + var i = from + breakable { + for (x <- this) { + if (i >= from && !p(x)) { result = i - from; break } + else i += 1 + } + } + result + } + + /** Returns length of longest prefix of this seqence + * such that every element of the prefix satisfies predicate `p`. + * @note may not terminate for infinite-sized collections. + * @param p the predicate + */ + def prefixLength(p: A => Boolean) = segmentLength(p, 0) + + /** Returns index of the first element starting from a start index + * satisying a predicate, or -1, if none exists. + * + * @note may not terminate for infinite-sized collections. + * @param p the predicate + * @param from the start index + */ + def indexWhere(p: A => Boolean, from: Int): Int = { + var result = -1 + var i = from + breakable { + for (x <- this) { + if (i >= from && p(x)) { result = i; break } + else i += 1 + } + } + result + } + + /** Returns index of the first element satisying a predicate, or -1. + * + * @deprecated Use `indexWhere` instead + */ + @deprecated def findIndexOf(p: A => Boolean): Int = indexWhere(p) + + /** Returns the index of the first occurence of the specified + * object in this iterable object. + * + * @note may not terminate for infinite-sized collections. + * @param elem element to search for. + * @return the index in this sequence of the first occurence of the + * specified element, or -1 if the sequence does not contain + * this element. + */ + def indexOf[B >: A](elem: B): Int = indexOf(elem, 0) + + /** Returns the index of the first occurence of the specified + * object in this iterable object, starting from a start index, or + * -1, if none exists. + * + * @note may not terminate for infinite-sized collections. + * @param elem element to search for. + */ + def indexOf[B >: A](elem: B, from: Int): Int = indexWhere(elem ==, from) + + /** Returns the index of the last occurence of the specified element + * in this sequence, or -1 if the sequence does not contain this element. + * + * @param elem element to search for. + * @return the index in this sequence of the last occurence of the + * specified element, or -1 if the sequence does not contain + * this element. + */ + def lastIndexOf[B >: A](elem: B): Int = lastIndexOf(elem, length - 1) + + /** Returns the index of the last + * occurence of the specified element in this sequence + * before or at a given end index, + * or -1 if the sequence does not contain this element. + * + * @param elem element to search for. + * @param end the end index + */ + def lastIndexOf[B >: A](elem: B, end: Int): Int = { + var i = end + val it = reversedElements + while (it.hasNext && it.next != elem) i -= 1 + i + } + + /** Returns index of the last element satisying a predicate, or -1, if none exists. + * + * @param p the predicate + * @return the index of the last element satisfying p, + * or -1 if such an element does not exist + */ + def lastIndexWhere(p: A => Boolean): Int = lastIndexWhere(p, length - 1) + + /** Returns index of the last element not exceeding a given end index + * and satisying a predicate, or -1 if none exists. + * + * @param end the end index + * @param p the predicate + */ + def lastIndexWhere(p: A => Boolean, end: Int): Int = { + var i = end + val it = reversedElements + while (it.hasNext && (i > end || !p(it.next))) i -= 1 + i + } + + /** A sequence of type C consisting of all elements of + * this sequence in reverse order. + * @note the operation is implemented by building a new sequence + * from this(length - 1), ..., this(0) + * If random access is inefficient for the given sequence implementation, + * this operation should be overridden. + */ + def reverse: CC[A] = { + var xs: List[A] = List() + for (x <- this) + xs = x :: xs + val b = newBuilder[A] + for (x <- xs) + b += x + b.result + } + + /** The elements of this sequence in reversed order + */ + def reversedElements: Iterator[A] = reverse.elements + + /** + * Checks whether the argument sequence is contained at the + * specified index within the receiver object. + * + * If the both the receiver object, this and + * the argument, that are infinite sequences + * this method may not terminate. + * + * @return true if that is contained in + * this, at the specified index, otherwise false + * + * @see String.startsWith + */ + def startsWith[B](that: Sequence[B], offset: Int): Boolean = { + val i = elements.drop(offset) + val j = that.elements + while (j.hasNext && i.hasNext && i.next == j.next) {} + !j.hasNext + } + + /** + * Check whether the receiver object starts with the argument sequence. + * + * @return true if that is a prefix of this, + * otherwise false + * + * @see Sequence.startsWith + */ + def startsWith[B](that: Sequence[B]): Boolean = startsWith(that, 0) + + /** @return true if this sequence end with that sequence + * @see String.endsWith + */ + def endsWith[B](that: Sequence[B]): Boolean = { + val i = this.elements.drop(length - that.length) + val j = that.elements + while (i.hasNext && j.hasNext && i.next == j.next) () + !j.hasNext + } + + /** @return -1 if that not contained in this, otherwise the + * index where that is contained + * @see String.indexOf + */ + def indexOf[B >: A](that: Sequence[B]): Int = { + var i = 0 + var s: Sequence[A] = thisCC + while (!s.isEmpty && !(s startsWith that)) { + i += 1 + s = s drop 1 + } + if (!s.isEmpty || that.isEmpty) i else -1 + } + + /** Tests if the given value elem is a member of this + * sequence. + * + * @param elem element whose membership has to be tested. + * @return true iff there is an element of this sequence + * which is equal (w.r.t. ==) to elem. + */ + def contains(elem: Any): Boolean = exists (_ == elem) + + /** Computes the union of this sequence and the given sequence + * that. + * + * @param that the sequence of elements to add to the sequence. + * @return an sequence containing the elements of this + * sequence and those of the given sequence that + * which are not contained in this sequence. + */ + def union[B >: A](that: Sequence[B]): CC[B] = this ++ (that diff thisCC) + + /** Computes the difference between this sequence and the given sequence + * that. + * + * @param that the sequence of elements to remove from this sequence. + * @return this sequence without the elements of the given sequence + * that. + */ + def diff[B >: A](that: Sequence[B]): CC[A] = this remove (that contains _) + + /** Computes the intersection between this sequence and the given sequence + * that. + * + * @param that the sequence to intersect. + * @return the sequence of elements contained both in this sequence and + * in the given sequence that. + */ + def intersect[B >: A](that: Sequence[B]): CC[A] = this filter(that contains _) + + /** Builds a new sequence from this sequence in which any duplicates (wrt to ==) removed. + * Among duplicate elements, only the first one is retained in the result sequence + */ + def removeDuplicates: CC[A] = { + val b = newBuilder[A] + var seen = Set[A]() + for (x <- this) { + if (!(seen contains x)) { + b += x + seen += x + } + } + b.result + } + + /** Returns a sequence of given length containing the elements of this sequence followed by zero + * or more occurrences of given elements. If this sequence is already at least as long as given + * length, it is returned directly. Otherwise, a new sequence is created consisting of the elements + * of this sequence followed by enough occurrences of the given elements to reach the given length. + */ + def padTo[B >: A](len: Int, elem: B): CC[B] = { + var diff = len - length + val b = newBuilder[B] //!!! drop [B] and get surprising results! + b ++= thisCC + while (diff > 0) { + b += elem + diff -=1 + } + b.result + } + + /** + * Overridden for efficiency. + * + * @return the sequence itself + */ + override def toSequence: Sequence[A] = this.asInstanceOf[Null] // !!! + + def indices: Range = 0 until length + + /** Creates a view of this iterable @see OrderedIterable.View + */ + override def view: SequenceView[CC, A] = new SequenceView[CC, A] { // !!! Martin: We should maybe infer the type parameters here? + val origin: Sequence[_] = thisCC + val elements: Iterator[A] = self.elements + val length: Int = self.length + def apply(idx: Int): A = self.apply(idx) + } + + /** A sub-sequence view starting at index `from` + * and extending up to (but not including) index `until`. + * + * @param from The index of the first element of the slice + * @param until The index of the element following the slice + * @note The difference between `view` and `slice` is that `view` produces + * a view of the current sequence, whereas `slice` produces a new sequence. + * + * @note view(from, to) is equivalent to view.slice(from, to) + */ + override def view(from: Int, until: Int): SequenceView[CC, A] = view.slice(from, until) + + /** Returns index of the last element satisying a predicate, or -1. + * @deprecated use `lastIndexWhere` instead + */ + @deprecated def findLastIndexOf(p: A => Boolean): Int = lastIndexWhere(p) + + /** A sub-sequence starting at index from + * and extending up to the length of the current sequence + * + * @param from The index of the first element of the slice + * @throws IndexOutOfBoundsException if from < 0 + * @deprecated use drop instead + */ + @deprecated def slice(from: Int): Sequence[A] = slice(from, length) + + /** @deprecated Should be replaced by + * + * (s1, s2) forall { case (x, y) => f(x, y) } + */ + @deprecated def equalsWith[B](that: Sequence[B])(f: (A,B) => Boolean): Boolean = { + val i = this.elements + val j = that.elements + while (i.hasNext && j.hasNext && f(i.next, j.next)) () + !i.hasNext && !j.hasNext + } + + /** Is that a slice in this? + * @deprecated Should be repaced by indexOf(that) != -1 + */ + @deprecated def containsSlice[B](that: Sequence[B]): Boolean = indexOf(that) != -1 + +} diff --git a/src/library/scalax/collection/generic/SequenceView.scala b/src/library/scalax/collection/generic/SequenceView.scala new file mode 100755 index 0000000000..6178542241 --- /dev/null +++ b/src/library/scalax/collection/generic/SequenceView.scala @@ -0,0 +1,129 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Sequence.scala 16092 2008-09-12 10:37:06Z nielsen $ + + +package scalax.collection.generic + +import util.control.Break._ +import annotation.unchecked.uncheckedVariance + +import Sequence._ + +/** A non-strict projection of an iterable. + * @author Sean McDirmid + * @author Martin Odersky + * @note this should really be a virtual class of SequenceFactory + */ +trait SequenceView[+UC[/*+*/B] <: Sequence[B], /*+*/A] extends IterableView[UC, A] with Sequence[A] { +self => + + /** refined from Iterable.View */ + val origin: Sequence[_] + + trait Transformed[/*+*/B] extends SequenceView[UC, B] { + val origin = self + protected def asCC = asInstanceOf[SequenceView[UC, B]] + } + + class Appended[/*+*/B >: A](that: Sequence[B]) extends Transformed[B] { + override def elements: Iterator[B] = self.elements ++ that.elements + override def length: Int = self.length + that.length + override def apply(idx: Int): B = { + val ll = self.length + if (idx < ll) self.apply(idx) else that.apply(idx - ll) + } + override def stringPrefix = self.stringPrefix + "A" + } + + class Sliced(from: Int, to: Int) extends Transformed[A] { + override def elements: Iterator[A] = self.elements slice (from, to) + override lazy val length: Int = ((to min self.length) - (from max 0) min 0) + override def apply(idx: Int): A = + if (idx >= 0 && idx < length) self.apply(idx + from) + else throw new IndexOutOfBoundsException(idx.toString) + override def stringPrefix = self.stringPrefix + "S" + override def slice(from1: Int, to1: Int) = + new self.Sliced(from + (from1 min length max 0) , to + (to1 min length max 0)).asInstanceOf[SequenceView[UC, A]] + } + + class Mapped[/*+*/B](f: A => B) extends Transformed[B] { + override def elements: Iterator[B] = self.elements map f + override def length: Int = self.length + override def apply(idx: Int): B = f(self.apply(idx)) + override def stringPrefix = self.stringPrefix + "M" + } + + class Reversed extends Transformed[A] { + override def elements: Iterator[A] = self.reversedElements + override def length: Int = self.length + override def apply(idx: Int): A = self.apply(length - 1 - idx) + override def stringPrefix = super.stringPrefix+"R" + } + + class Patched[/*+*/B >: A](from: Int, patch: Sequence[B], replaced: Int) extends Transformed[B] { + val plen = patch.length + override def elements: Iterator[B] = self.elements patch (from, patch.asInstanceOf[Null], replaced) // !!! + override def length: Int = self.length + plen - replaced + override def apply(idx: Int): B = + if (idx < from) self.apply(idx) + else if (idx < from + plen) patch.apply(idx - from) + else self.apply(idx - plen + replaced) + override def stringPrefix = super.stringPrefix+"P" + } + + class Zipped[/*+*/B](that: Sequence[B]) extends Transformed[(A, B)] { + override def elements: Iterator[(A, B)] = self.elements zip that.elements + override def length = self.length min that.length + override def apply(idx: Int): (A, B) = (self.apply(idx), that.apply(idx)) + override def stringPrefix = super.stringPrefix+"Z" + } + + override def ++[B >: A](that: Iterable[B]): SequenceView[UC, B] = + new Appended[B](that.toSequence).asCC + override def ++[B >: A](that: Iterator[B]): SequenceView[UC, B] = + new Appended[B](that.toSequence).asCC + override def map[B](f: A => B): SequenceView[UC, B] = + new Mapped(f).asCC + override def reverse: SequenceView[UC, A] = + (new Reversed).asCC + def patch[B >: A](from: Int, patch: Sequence[B], replaced: Int): SequenceView[UC, B] = + if (0 <= from && from < length) new Patched(from, patch, replaced).asCC + else throw new IndexOutOfBoundsException(from.toString) + override def padTo[B >: A](len: Int, elem: B): SequenceView[UC, B] = + patch(length, fill((len - length) max 0, elem), 0) + override def zip[B](that: Iterable[B]): SequenceView[UC, (A, B)] = + new Zipped(that.toSequence).asCC + override def zipWithIndex: SequenceView[UC, (A, Int)] = + zip((0 until length).asInstanceOf[Null]) // !!! + /* + override def zipAll[B, A1 >: A, B1 >: B](that: Iterable[B], thisElem: A1, thatElem: B1): SequenceView[UC, (A1, B1)] = { + val that1 = that.toSequence + self.padTo(that1.length, thisElem) zip that1.padTo(this.length, thatElem)//.asInstanceOf[SequenceView[UC, (A1, B1)]] + }*/ + override def take(n: Int): SequenceView[UC, A] = + slice(0, n) + override def drop(n: Int): SequenceView[UC, A] = + slice(n, Math.MAX_INT) + override def splitAt(n: Int): (SequenceView[UC, A], SequenceView[UC, A]) = (take(n), drop(n)) + override def slice(from: Int, until: Int): SequenceView[UC, A] = + new Sliced(from, until).asCC + override def takeWhile(p: A => Boolean): SequenceView[UC, A] = + take(prefixLength(p)) + override def dropWhile(p: A => Boolean): SequenceView[UC, A] = + drop(prefixLength(p)) + override def span(p: A => Boolean): (SequenceView[UC, A], SequenceView[UC, A]) = { + val n = prefixLength(p) + (take(n), drop(n)) + } + + // missing here because we can't do anything about them, so we fall back to default construction + // if an IterableView via newView: flatMap, filter, partition +} + diff --git a/src/library/scalax/collection/generic/VectorTemplate.scala b/src/library/scalax/collection/generic/VectorTemplate.scala new file mode 100644 index 0000000000..bc18ac45c2 --- /dev/null +++ b/src/library/scalax/collection/generic/VectorTemplate.scala @@ -0,0 +1,264 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2006-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Vector.scala 15437 2008-06-25 16:22:45Z stepancheg $ + +package scalax.collection.generic + +import Vector._ + +/** Sequences that support O(1) element access and O(1) length computation. + * @author Sean McDirmid + * @author Martin Odersky + * @version 2.8 + */ +trait VectorTemplate[+CC[/*+*/B] <: VectorTemplate[CC, B] with Vector[B], /*+*/A] extends SequenceTemplate[CC, A] { +self => + + // Overridden methods from IterableTemplate + + /** The iterator returned by the elements method + */ + protected class Elements(start: Int, end: Int) extends scalax.collection.BufferedIterator[A] { + var i = start + def hasNext: Boolean = i < end + def next: A = + if (i < end) { + val x = self(i) + i += 1 + x + } else Iterator.empty.next + def head = + self(i) + /** drop is overridden to enable fast searching in the middle of random access sequences */ + override def drop(n: Int): Iterator[A] = + if (n > 0) new Elements(start + n, end) else this + /** is overridden to be symmetric to drop */ + override def take(n: Int): Iterator[A] = + if (n < 0) Iterator.empty.buffered + else if (start + n < end) new Elements(start, start + n) + else this + } + + override def elements: Iterator[A] = new Elements(0, length) + + override def isEmpty: Boolean = { length == 0 } + + override def foreach(f: A => Unit): Unit = { + var i = 0 + val len = length + while (i < len) { f(this(i)); i += 1 } + } + + override def forall(p: A => Boolean): Boolean = prefixLength(p(_)) == length + override def exists(p: A => Boolean): Boolean = prefixLength(!p(_)) != length + + override def find(p: A => Boolean): Option[A] = { + val i = prefixLength(!p(_)) + if (i < length) Some(this(i)) else None + } + + private def foldl[B](start: Int, z: B, op: (B, A) => B): B = { + var i = start + val len = length + var result = z + while (i < len) { + result = op(result, this(i)) + i += 1 + } + result + } + + private def foldr[B](start: Int, len: Int, z: B, op: (A, B) => B): B = + if (start == len) z + else op(this(start), foldr(start + 1, len, z, op)) + + override def foldLeft[B](z: B)(op: (B, A) => B): B = foldl(0, z, op) + override def foldRight[B](z: B)(op: (A, B) => B): B = foldr(0, length, z, op) + override def reduceLeft[B >: A](op: (B, A) => B): B = + if (length > 0) foldl(0, this(0), op) else super.reduceLeft(op) + override def reduceRight[B >: A](op: (A, B) => B): B = + if (length > 0) foldr(0, length - 1, this(length - 1), op) else super.reduceRight(op) + + override def zip[B](that: Iterable[B]): CC[(A, B)] = that match { + case that: Vector[_] => + var i = 0 + val len = this.length min that.length + val b = this.newBuilder[(A, B)] + while (i < len) { + b += ((this(i), that(i).asInstanceOf[B])) + i += 1 + } + b.result + case _ => + super.zip(that) + } + + override def zipAll[B, A1 >: A, B1 >: B](that: Iterable[B], thisElem: A1, thatElem: B1): CC[(A1, B1)] = that match { + case that: Vector[_] => + var i = 0 + val len = this.length min that.length + val b = this.newBuilder[(A1, B1)] + while (i < len) { + b += ((this(i), that(i).asInstanceOf[B])) + i += 1 + } + while (i < this.length) { + b += ((this(i), thatElem)) + i += 1 + } + while (i < that.length) { + b += ((this(i), thatElem.asInstanceOf[B])) + i += 1 + } + b.result + case _ => + super.zipAll(that, thisElem, thatElem) + } + + override def zipWithIndex: CC[(A, Int)] = { + val b = newBuilder[(A, Int)] + var i = 0 + val len = length + while (i < len) { + b += ((this(i), i)) + i += 1 + } + b.result + } + + override def copyToArray[B >: A](xs: Array[B], start: Int, len: Int) { + var i = 0 + var j = start + val end = length min len min (xs.length - start) + while (i < end) { + xs(j) = this(i) + i += 1 + j += 1 + } + } + + // Overridden methods from OrderedIterableTemplate + + override def head: A = if (isEmpty) throw new NoSuchElementException else this(0) + + override def slice(from: Int, until: Int): CC[A] = { + val b = newBuilder[A] + var i = from max 0 + val end = until min length + while (i < end) { + b += this(i) + i += 1 + } + b.result + } + + override def take(n: Int): CC[A] = slice(0, n) + override def drop(n: Int): CC[A] = slice(n, length) + override def takeRight(n: Int): CC[A] = slice(length - n, length) + override def dropRight(n: Int): CC[A] = slice(0, length - n) + override def splitAt(n: Int): (CC[A], CC[A]) = (take(n), drop(n)) + override def takeWhile(p: A => Boolean): CC[A] = take(prefixLength(p)) + override def dropWhile(p: A => Boolean): CC[A] = drop(prefixLength(p)) + override def span(p: A => Boolean): (CC[A], CC[A]) = splitAt(prefixLength(p)) + override def last: A = if (length > 0) this(length - 1) else super.last + override def init: CC[A] = if (length > 0) slice(0, length - 1) else super.init + + override def sameElements[B >: A](that: OrderedIterable[B]): Boolean = that match { + case that: Vector[_] => + val len = length + len == that.length && { + var i = 0 + while (i < len && this(i) == that(i)) i += 1 + i == len + } + case _ => + super.sameElements(that) + } + + // Overridden methods from Sequence + + override def lengthCompare(len: Int): Int = length - len + + private def negLength(n: Int) = if (n == length) -1 else n + + override def indexWhere(p: A => Boolean, from: Int): Int = + negLength(from + segmentLength(!p(_), from)) + + override def lastIndexWhere(p: A => Boolean, end: Int): Int = { + var i = end + while (i >= 0 && !p(this(i))) i -= 1 + i + } + + override def lastIndexOf[B >: A](elem: B, end: Int): Int = lastIndexWhere(elem ==, end) + + override def reverse: CC[A] = { + val b = newBuilder[A] + var i = length + while (0 < i) { + i -= 1 + b += this(i) + } + b.result + } + + override def reversedElements: Iterator[A] = new Iterator[A] { + var i = length + def hasNext: Boolean = 0 < i + def next: A = + if (0 < i) { + i -= 1 + self(i) + } else Iterator.empty.next + } + + override def startsWith[B](that: Sequence[B], offset: Int): Boolean = that match { + case that: Vector[_] => + var i = offset + var j = 0 + val thisLen = length + val thatLen = that.length + while (i < thisLen && j < thatLen && this(i) == that(j)) { + i += 1 + j += 1 + } + j == thatLen + case _ => + var i = offset + val thisLen = length + val thatElems = that.elements + while (i < thisLen && thatElems.hasNext && this(i) == thatElems.next) { + i += 1 + } + !thatElems.hasNext + } + + override def endsWith[B](that: Sequence[B]): Boolean = that match { + case that: Vector[_] => + val thisLen = length + val thatLen = that.length + var i = thisLen - 1 + var j = thatLen - 1 + while (i >= 0 && j >= 0 && this(i) == that(j)) { + i -= 1 + j -= 1 + } + j == 0 + case _ => + super.endsWith(that) + } + + override def indexOf[B >: A](that: Sequence[B]): Int = { + var i = 0 + val last = length - that.length + while (i <= last && !startsWith(that, i)) i += 1 + negLength(i) + } +} + diff --git a/src/library/scalax/collection/generic/covariant/IterableFactory.scala b/src/library/scalax/collection/generic/covariant/IterableFactory.scala new file mode 100755 index 0000000000..67445f54d9 --- /dev/null +++ b/src/library/scalax/collection/generic/covariant/IterableFactory.scala @@ -0,0 +1,14 @@ +package scalax.collection.generic.covariant + +trait IterableFactory[CC[+A] <: Iterable[A]] extends generic.IterableFactory[CC] { + + /** The empty collection of type CC */ + val empty: CC[Nothing] + + override protected def newBuilder[A]: Builder[CC, A] = + empty.newBuilder[A].asInstanceOf[Builder[CC, A]] + + /** Create CC collection of specified elements */ + override def apply[A](args: A*): CC[A] = + (empty ++ args.asInstanceOf[Iterable[A]]).asInstanceOf[CC[A]] // !!! +} diff --git a/src/library/scalax/collection/generic/covariant/IterableTemplate.scala b/src/library/scalax/collection/generic/covariant/IterableTemplate.scala new file mode 100755 index 0000000000..41155a5ad5 --- /dev/null +++ b/src/library/scalax/collection/generic/covariant/IterableTemplate.scala @@ -0,0 +1,29 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Iterable.scala 15188 2008-05-24 15:01:02Z stepancheg $ + + +package scalax.collection.generic.covariant + +import annotation.unchecked.uncheckedVariance + +/** Collection classes mixing in this class provide a method + * elements which returns an iterator over all the + * elements contained in the collection. + * + * @note If a collection has a known size, it should also sub-type Collection. + * Only potentially unbounded collections should directly sub-class Iterable. + * @author Matthias Zenger + * @version 1.1, 04/02/2004 + */ +trait IterableTemplate[+CC[+B] <: IterableTemplate[CC, B] with Iterable[B], +A] + extends generic.IterableTemplate[CC, A @uncheckedVariance] { self /*: CC[A]*/ => } + +// !!! todo: explain why @uncheckedVariance is justified here. + diff --git a/src/library/scalax/collection/generic/covariant/IterableView.scala b/src/library/scalax/collection/generic/covariant/IterableView.scala new file mode 100755 index 0000000000..7ccad07405 --- /dev/null +++ b/src/library/scalax/collection/generic/covariant/IterableView.scala @@ -0,0 +1,21 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Iterable.scala 15188 2008-05-24 15:01:02Z stepancheg $ + +package scalax.collection.generic.covariant + +import annotation.unchecked.uncheckedVariance + +/** A non-strict projection of an iterable. + * @author Sean McDirmid + * @author Martin Odersky + * @note this should really be a virtual class of SequenceFactory + */ +trait IterableView[+UC[+B] <: Iterable[B], +A] + extends generic.IterableView[UC, A @uncheckedVariance] diff --git a/src/library/scalax/collection/generic/covariant/OrderedIterableTemplate.scala b/src/library/scalax/collection/generic/covariant/OrderedIterableTemplate.scala new file mode 100755 index 0000000000..449bac9cfc --- /dev/null +++ b/src/library/scalax/collection/generic/covariant/OrderedIterableTemplate.scala @@ -0,0 +1,17 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Iterable.scala 15188 2008-05-24 15:01:02Z stepancheg $ + + +package scalax.collection.generic.covariant + +import annotation.unchecked.uncheckedVariance + +trait OrderedIterableTemplate[+CC[+B] <: OrderedIterableTemplate[CC, B] with OrderedIterable[B], +A] + extends generic.OrderedIterableTemplate[CC, A @uncheckedVariance] {self /*: CC[A]*/ => } diff --git a/src/library/scalax/collection/generic/covariant/SequenceFactory.scala b/src/library/scalax/collection/generic/covariant/SequenceFactory.scala new file mode 100755 index 0000000000..49a6e685fa --- /dev/null +++ b/src/library/scalax/collection/generic/covariant/SequenceFactory.scala @@ -0,0 +1,3 @@ +package scalax.collection.generic.covariant + +trait SequenceFactory[CC[+A] <: Sequence[A]] extends IterableFactory[CC] with generic.SequenceFactory[CC] diff --git a/src/library/scalax/collection/generic/covariant/SequenceTemplate.scala b/src/library/scalax/collection/generic/covariant/SequenceTemplate.scala new file mode 100755 index 0000000000..958e73cd47 --- /dev/null +++ b/src/library/scalax/collection/generic/covariant/SequenceTemplate.scala @@ -0,0 +1,17 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Sequence.scala 16092 2008-09-12 10:37:06Z nielsen $ + + +package scalax.collection.generic.covariant + +import annotation.unchecked.uncheckedVariance + +trait SequenceTemplate[+CC[+B] <: SequenceTemplate[CC, B] with Sequence[B], +A] + extends generic.SequenceTemplate[CC, A @uncheckedVariance] {self /*: CC[A]*/ => } diff --git a/src/library/scalax/collection/generic/covariant/SequenceView.scala b/src/library/scalax/collection/generic/covariant/SequenceView.scala new file mode 100755 index 0000000000..cfcf9d4a25 --- /dev/null +++ b/src/library/scalax/collection/generic/covariant/SequenceView.scala @@ -0,0 +1,22 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Sequence.scala 16092 2008-09-12 10:37:06Z nielsen $ + + +package scalax.collection.generic.covariant + +import annotation.unchecked.uncheckedVariance + +/** A non-strict projection of an iterable. + * @author Sean McDirmid + * @author Martin Odersky + * @note this should really be a virtual class of SequenceFactory + */ +trait SequenceView[+UC[+B] <: Sequence[B], +A] + extends IterableView[UC, A] with generic.SequenceView[UC, A @uncheckedVariance] diff --git a/src/library/scalax/collection/generic/covariant/VectorTemplate.scala b/src/library/scalax/collection/generic/covariant/VectorTemplate.scala new file mode 100644 index 0000000000..1f75c68e82 --- /dev/null +++ b/src/library/scalax/collection/generic/covariant/VectorTemplate.scala @@ -0,0 +1,17 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Sequence.scala 16092 2008-09-12 10:37:06Z nielsen $ + + +package scalax.collection.generic.covariant + +import annotation.unchecked.uncheckedVariance + +trait VectorTemplate[+CC[+B] <: VectorTemplate[CC, B] with Vector[B], +A] + extends generic.VectorTemplate[CC, A @uncheckedVariance] {self /*: CC[A]*/ => } diff --git a/src/library/scalax/collection/generic/covartest/IterableFactory.scala b/src/library/scalax/collection/generic/covartest/IterableFactory.scala new file mode 100755 index 0000000000..d12da89987 --- /dev/null +++ b/src/library/scalax/collection/generic/covartest/IterableFactory.scala @@ -0,0 +1,108 @@ +package scalax.collection.generic.covartest + +trait IterableFactory[CC[A] <: Iterable[A]] { + + /** Create CC collection of specified elements */ + def apply[A](args: A*): CC[A] + + protected def newBuilder[A]: Builder[CC, A] = + apply().newBuilder[A].asInstanceOf[Builder[CC, A]] + + /** Concatenate all the argument lists into a single list. + * + * @param xss the lists that are to be concatenated + * @return the concatenation of all the lists + */ + def concat[A](xss: CC[A]*): CC[A] = { + val b = newBuilder[A] + for (xs <- xss) b ++= xs + b.result + } + + /** An iterable that contains the same element a number of times + * @param n The number of elements returned + * @param elem The element returned each time + */ + def fill[A](n: Int, elem: => A): CC[A] = { + val b = newBuilder[A] + var i = 0 + while (i < n) { + b += elem + i += 1 + } + b.result + } + + def fill[A](n1: Int, n2: Int, elem: => A): CC[CC[A]] = + tabulate(n1)(_ => fill(n2, elem)) + + def fill[A](n1: Int, n2: Int, n3: Int, elem: => A): CC[CC[CC[A]]] = + tabulate(n1)(_ => fill(n2, n3, elem)) + + def tabulate[A](n: Int)(f: Int => A): CC[A] = { + val b = newBuilder[A] + var i = 0 + while (i < n) { + b += f(i) + i += 1 + } + b.result + } + + def tabulate[A](n1: Int, n2: Int)(f: (Int, Int) => A): CC[CC[A]] = + tabulate(n1)(i1 => tabulate(n2)(i2 => f(i1, i2))) + + def tabulate[A](n1: Int, n2: Int, n3: Int)(f: (Int, Int, Int) => A): CC[CC[CC[A]]] = + tabulate(n1)(i1 => tabulate(n2)(i2 => tabulate(n3)(i3 => f(i1, i2, i3)))) +// todo: go up to 5(?) + + /** Create a sequence of increasing integers in a range. + * + * @param from the start value of the sequence + * @param end the end value of the sequence + * @return the sorted list of all from `from` (inclusive) + * up to, but exclusding, `end`. + */ + def range[A](start: Int, end: Int): CC[Int] = range(start, end, 1) + + /** Create a sequence of increasing integers in a range. + * + * @param from the start value of the sequence + * @param end the end value of the sequence + * @param step the increment value of successive elements + * @return a list of values from + n * step for + * increasing n. If `step > 0` the sequence terminates + * with the largest value less than `end`. If `step < 0` + * the sequence terminates with the smallest value greater than `end`. + * If `step == 0`, the sequence gors on infinitely (in that + * case the `range` operation might not terminate. + */ + def range(start: Int, end: Int, step: Int): CC[Int] = { + val b = newBuilder[Int] + var i = start + while ((step <= 0 || i < end) && (step >= 0 || i > end)) { + b += i + i += step + } + b.result + } + + /** Create a sequence by repeatedly applying a given function to a start value. + * + * @param start the start value of the sequence + * @param len the length of the sequence + * @param f the function that's repeatedly applied + * @return the sequence with elements (start, f(start), f(f(start)), ..., flen-1(start)) + */ + def iterate(start: Int, len: Int)(f: Int => Int): CC[Int] = { + val b = newBuilder[Int] + var acc = start + var i = 0 + while (i < len) { + b += acc + acc = f(acc) + i += 1 + } + b.result + } +} diff --git a/src/library/scalax/collection/generic/covartest/IterableTemplate.scala b/src/library/scalax/collection/generic/covartest/IterableTemplate.scala new file mode 100755 index 0000000000..b83f3b9247 --- /dev/null +++ b/src/library/scalax/collection/generic/covartest/IterableTemplate.scala @@ -0,0 +1,816 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Iterable.scala 15188 2008-05-24 15:01:02Z stepancheg $ + + +package scalax.collection.generic.covartest + +import scalax.collection.mutable.{Buffer, ArrayBuffer, ListBuffer} +import scalax.collection.immutable.{List, Nil, ::} +import util.control.Break._ +import Iterable._ + +/** Collection classes mixing in this class provide a method + * elements which returns an iterator over all the + * elements contained in the collection. + * + * @note If a collection has a known size, it should also sub-type Collection. + * Only potentially unbounded collections should directly sub-class Iterable. + * @author Matthias Zenger + * @author Martin Odersky + * @version 2.8 + */ +trait IterableTemplate[+CC[+B] <: IterableTemplate[CC, B] with Iterable[B], +A] { self/*: CC[A]*/ => + + /** The template itself seen as an instance of `CC[A]`. + * @note: It would be logical to have a self type `CC[A]` instead, then we would not need + * this method. Unfortunately, tyis runs afoul some pecularities in Scala's member resolution + * algorithm: If the self type is a CC, then Iterable is one of its supertypes. Iterable + * defines a number of concrete methods such as newBuilder which are abstract here. + * The newBuilder method in Iterable[A] has type Builder[Iterable, A]. Because Scala + * prefers concrete over abstract members, it is this newBuilder which is chosen, instead of + * the abstract newBuilder in class IterableTemplate of type Builder[CC, A]. + * Even for concrete methods we have a problem because the last mixin in the parents of CC is + * Iterable, not IterableTemplate. So resolution picks the version in Iterable, which returns + * again an Iterable, not a CC, as would be required. + * These problems would be avoided if Scala computed the type of a member as the glb of the types + * all members in the class and its superclasses types. + * I think overall this would be a better design. + */ + protected[this] def thisCC: CC[A] = this.asInstanceOf[CC[A]] + + /** Creates a new iterator over all elements contained in this + * object. + * + * @return the new iterator + */ + def elements: Iterator[A] + + /** Create a new builder for this IterableType + */ + def newBuilder[B]: Builder[CC, B] + + /** Is this collection empty? */ + def isEmpty: Boolean = !elements.hasNext + + /** returns true iff this collection has a bound size. + * Many APIs in this trait will not work on collections of + * unbound sizes. + */ + def hasDefiniteSize = true + + /** Create a new sequence of type CC which contains all elements of this sequence + * followed by all elements of Iterable `that' + */ + def ++[B >: A](that: Iterable[B]): CC[B] = { + val b: Builder[CC, B] = (this: IterableTemplate[CC, A]).newBuilder[B] + b ++= thisCC + b ++= that + b.result + } + + /** Create a new sequence of type IterableType which contains all elements of this sequence + * followed by all elements of Iterator `that' + */ + def ++[B >: A](that: Iterator[B]): CC[B] = { + val b = newBuilder[B] + b ++= thisCC + b ++= that + b.result + } + + /** Returns the sequence resulting from applying the given function + * f to each element of this sequence. + * + * @param f function to apply to each element. + * @return f(a0), ..., f(an) if this + * sequence is a0, ..., an. + */ + def map[B](f: A => B): CC[B] = { + val b = newBuilder[B] + for (x <- this) b += f(x) + b.result + } + + /** Applies the given function f to each element of + * this sequence, then concatenates the results. + * + * @param f the function to apply on each element. + * @return f(a0) ::: ... ::: f(an) if + * this sequence is a0, ..., an. + */ + def flatMap[B](f: A => Iterable[B]): CC[B] = { + val b = newBuilder[B] + for (x <- this) b ++= f(x) + b.result + } + + /** Returns all the elements of this sequence that satisfy the + * predicate p. The order of the elements is preserved. + * @param p the predicate used to filter the list. + * @return the elements of this list satisfying p. + */ + def filter(p: A => Boolean): CC[A] = { + val b = newBuilder[A] + for (x <- this) + if (p(x)) b += x + b.result + } + + /** Removes all elements of the iterable which satisfy the predicate + * p. This is like filter with the + * predicate inversed. + * + * @param p the predicate to use to test elements + * @return the list without all elements which satisfy p + */ + def remove(p: A => Boolean): CC[A] = filter(!p(_)) + + /** Partitions this iterable in two iterables according to a predicate. + * + * @param p the predicate on which to partition + * @return a pair of iterables: the iterable that satisfies the predicate + * p and the iterable that does not. + * The relative order of the elements in the resulting iterables + * is the same as in the original iterable. + */ + def partition(p: A => Boolean): (CC[A], CC[A]) = { + val l, r = newBuilder[A] + for (x <- this) (if (p(x)) l else r) += x + (l.result, r.result) + } + + /** Apply a function f to all elements of this + * iterable object. + * + * @note Will not terminate for infinite-sized collections. + * @param f a function that is applied to every element. + * Note this function underlies the implementation of most other bulk operations. + * It should be overridden in concrete collectionc classes with efficient implementations. + */ + def foreach(f: A => Unit): Unit = elements.foreach(f) + + /** Return true iff the given predicate `p` yields true for all elements + * of this iterable. + * + * @note May not terminate for infinite-sized collections. + * @param p the predicate + */ + def forall(p: A => Boolean): Boolean = { + var result = true + breakable { + for (x <- this) + if (!p(x)) { result = false; break } + } + result + } + + /** Return true iff there is an element in this iterable for which the + * given predicate `p` yields true. + * + * @note May not terminate for infinite-sized collections. + * @param p the predicate + */ + def exists(p: A => Boolean): Boolean = { + var result = false + breakable { + for (x <- this) + if (p(x)) { result = true; break } + } + result + } + + /** Count the number of elements in the iterable which satisfy a predicate. + * + * @param p the predicate for which to count + * @return the number of elements satisfying the predicate p. + */ + def count(p: A => Boolean): Int = { + var cnt = 0 + for (x <- this) { + if (p(x)) cnt += 1 + } + cnt + } + + /** Find and return the first element of the iterable object satisfying a + * predicate, if any. + * + * @note may not terminate for infinite-sized collections. + * @param p the predicate + * @return an option containing the first element in the iterable object + * satisfying p, or None if none exists. + */ + def find(p: A => Boolean): Option[A] = { + var result: Option[A] = None + breakable { + for (x <- this) + if (p(x)) { result = Some(x); break } + } + result + } + + /** Combines the elements of this iterable object together using the binary + * function f, from left to right, and starting with + * the value z. + * + * @note Will not terminate for infinite-sized collections. + * @return f(... (f(f(z, a0), a1) ...), + * an) if the list is + * [a0, a1, ..., an]. + */ + def foldLeft[B](z: B)(op: (B, A) => B): B = { + var result = z + for (x <- this) + result = op(result, x) + result + } + + /** Combines the elements of this list together using the binary + * function f, from right to left, and starting with + * the value z. + * + * @note Will not terminate for infinite-sized collections. + * @return f(a0, f(a1, f(..., f(an, z)...))) + * if the list is [a0, a1, ..., an]. + */ + def foldRight[B](z: B)(op: (A, B) => B): B = elements.foldRight(z)(op) + + /** Similar to foldLeft but can be used as + * an operator with the order of list and zero arguments reversed. + * That is, z /: xs is the same as xs foldLeft z + * @note Will not terminate for infinite-sized collections. + */ + def /: [B](z: B)(op: (B, A) => B): B = foldLeft(z)(op) + + /** An alias for foldRight. + * That is, xs :\ z is the same as xs foldRight z + * @note Will not terminate for infinite-sized collections. + */ + def :\ [B](z: B)(op: (A, B) => B): B = foldRight(z)(op) + + /** Combines the elements of this iterable object together using the binary + * operator op, from left to right + * @note Will not terminate for infinite-sized collections. + * @param op The operator to apply + * @return op(... op(a0,a1), ..., an) + if the iterable object has elements + * a0, a1, ..., an. + * @throws Predef.UnsupportedOperationException if the iterable object is empty. + */ + def reduceLeft[B >: A](op: (B, A) => B): B = { + if (isEmpty) throw new UnsupportedOperationException("empty.reduceLeft") + var result: B = elements.next + var first = true + for (x <- this) + if (first) first = false + else result = op(result, x) + result + } + + /** Combines the elements of this iterable object together using the binary + * operator op, from right to left + * @note Will not terminate for infinite-sized collections. + * @param op The operator to apply + * + * @return a0 op (... op (an-1 op an)...) + * if the iterable object has elements a0, a1, ..., + * an. + * + * @throws Predef.UnsupportedOperationException if the iterator is empty. + */ + def reduceRight[B >: A](op: (A, B) => B): B = + elements.reduceRight(op) + + /** Returns an iterable formed from this iterable and the specified list + * `other` by associating each element of the former with + * the element at the same position in the latter. + * If one of the two iterables is longer than the other, its remaining elements are ignored. + */ + def zip[B](that: Iterable[B]): CC[(A, B)] = { + val these = this.elements + val those = that.elements + val b = this.newBuilder[(A, B)] + while (these.hasNext && those.hasNext) + b += ((these.next, those.next)) + b.result + } + + /** Returns a iterable formed from this iterable and the specified iterable + * that by associating each element of the former with + * the element at the same position in the latter. + * + * @param that iterable that may have a different length + * as the self iterable. + * @param thisElem element thisElem is used to fill up the + * resulting iterable if the self iterable is shorter than + * that +b * @param thatElem element thatElem is used to fill up the + * resulting iterable if that is shorter than + * the self iterable + * @return Iterable((a0,b0), ..., + * (an,bn), (elem,bn+1), + * ..., {elem,bm}) + * when [a0, ..., an] zip + * [b0, ..., bm] is + * invoked where m > n. + */ + def zipAll[B, A1 >: A, B1 >: B](that: Iterable[B], thisElem: A1, thatElem: B1): CC[(A1, B1)] = { + val these = this.elements + val those = that.elements + val b = newBuilder[(A1, B1)] + while (these.hasNext && those.hasNext) + b += ((these.next, those.next)) + while (these.hasNext) + b += ((these.next, thatElem)) + while (those.hasNext) + b += ((thisElem, those.next)) + b.result + } + + /** Zips this iterable with its indices. `s.zipWithIndex` is equivalent to + * `s zip s.indices`, but is usually more efficient. + */ + def zipWithIndex: CC[(A, Int)] = { + val b = newBuilder[(A, Int)] + var i = 0 + for (x <- this) { + b += ((x, i)) + i +=1 + } + b.result + } + + /** Copy all elements to a given buffer + * @note Will not terminate for infinite-sized collections. + * @param dest The buffer to which elements are copied + */ + def copyToBuffer[B >: A](dest: Buffer[B]) { + for (x <- this) dest += x + } + + /** Fills the given array xs with at most `len` elements of + * this sequence starting at position `start`. + * Copying will stop oce either the end of the current iterable is reached or + * `len` elements have been copied. + * + * @note Will not terminate for infinite-sized collections. + * @param xs the array to fill. + * @param start starting index. + * @param len number of elements to copy + * @pre the array must be large enough to hold all elements. + */ + def copyToArray[B >: A](xs: Array[B], start: Int, len: Int) { + var i = start + val end = (start + len) min xs.length + for (x <- this) { + if (i < end) { + xs(i) = x + i += 1 + } + } + } + + /** Fills the given array xs with the elements of + * this sequence starting at position start + * until either the end of the current iterable or the end of array `xs` is reached. + * + * @note Will not terminate for infinite-sized collections. + * @param xs the array to fill. + * @param start starting index. + * @pre the array must be large enough to hold all elements. + */ + def copyToArray[B >: A](xs: Array[B], start: Int) { + copyToArray(xs, start, xs.length - start) + } + + /** Converts this collection to a fresh Array elements. + * @note Will not terminate for infinite-sized collections. + */ + def toArray[B >: A]: Array[B] = { + var size = 0 + for (x <- this) size += 1 + val result = new Array[B](size) + copyToArray(result, 0) + result + } + + /** + * Create a fresh list with all the elements of this iterable object. + * @note Will not terminate for infinite-sized collections. + */ + def toList: List[A] = (new ListBuffer[A] ++ thisCC).toList + + /** + * Returns a sequence containing all of the elements in this iterable object. + * @note Will not terminate for infinite-sized collections. + */ + def toSequence: Sequence[A] = toList.asInstanceOf[Sequence[A]] // !!! + + /** @deprecated use toSequence instead + */ + @deprecated def toSeq: Sequence[A] = toSequence + + /** + * Create a stream which contains all the elements of this iterable object. + * @note consider using projection for lazy behavior. + */ + def toStream: Stream[A] = elements.toStream + + /** Sort the iterable according to the comparison function + * <(e1: a, e2: a) => Boolean, + * which should be true iff e1 is smaller than + * e2. + * The sort is stable. That is elements that are equal wrt `lt` appear in the + * same order in the sorted iterable as in the original. + * + * @param lt the comparison function + * @return a list sorted according to the comparison function + * <(e1: a, e2: a) => Boolean. + * @ex
+   *    List("Steve", "Tom", "John", "Bob")
+   *      .sort((e1, e2) => (e1 compareTo e2) < 0) =
+   *    List("Bob", "John", "Steve", "Tom")
+ * !!! + def sortWith(lt : (A,A) => Boolean): CC[A] = { + val arr = toArray + Array.sortWith(arr, lt) + val b = newBuilder[A] + for (x <- arr) b += x + b.result + } + */ + + /** Returns a string representation of this iterable object. The resulting string + * begins with the string start and is finished by the string + * end. Inside, the string representations of elements (w.r.t. + * the method toString()) are separated by the string + * sep. + * + * @ex List(1, 2, 3).mkString("(", "; ", ")") = "(1; 2; 3)" + * @note Will not terminate for infinite-sized collections. + * @param start starting string. + * @param sep separator string. + * @param end ending string. + * @return a string representation of this iterable object. + */ + def mkString(start: String, sep: String, end: String): String = + addString(new StringBuilder(), start, sep, end).toString + + /** Returns a string representation of this iterable object. The string + * representations of elements (w.r.t. the method toString()) + * are separated by the string sep. + * + * @note Will not terminate for infinite-sized collections. + * @param sep separator string. + * @return a string representation of this iterable object. + */ + def mkString(sep: String): String = + addString(new StringBuilder(), sep).toString + + /** Converts a collection into a flat String by each element's toString method. + * @note Will not terminate for infinite-sized collections. + */ + def mkString = + addString(new StringBuilder()).toString + + /** Write all elements of this iterable into given string builder. + * The written text begins with the string start and is finished by the string + * end. Inside, the string representations of elements (w.r.t. + * the method toString()) are separated by the string + * sep. + * @note Will not terminate for infinite-sized collections. + */ + def addString(b: StringBuilder, start: String, sep: String, end: String): StringBuilder = { + b append start + var first = true + for (x <- this) { + if (first) first = false + else b append sep + b append x + } + b append end + } + + /** Write all elements of this string into given string builder. + * The string representations of elements (w.r.t. the method toString()) + * are separated by the string sep. + * @note Will not terminate for infinite-sized collections. + */ + def addString(b: StringBuilder, sep: String): StringBuilder = { + var first = true + for (x <- this) { + if (first) first = false + else b append sep + b append x + } + b + } + + /** Write all elements of this string into given string builder without using + * any separator between consecutive elements. + * @note Will not terminate for infinite-sized collections. + */ + def addString(b: StringBuilder): StringBuilder = { + for (x <- this) { + b append x + } + b + } + + /** + * returns a projection that can be used to call non-strict filter, + * map, and flatMap methods that build projections + * of the collection. + def projection : Iterable.Projection[A] = new Iterable.Projection[A] { + def elements = Iterable.this.elements + override def force = Iterable.this + } + */ + + override def toString = mkString(stringPrefix + "(", ", ", ")") + + /** Defines the prefix of this object's toString representation. + */ + def stringPrefix : String = { + var string = this.getClass.getName + val idx1 = string.lastIndexOf('.' : Int) + if (idx1 != -1) string = string.substring(idx1 + 1) + val idx2 = string.indexOf('$') + if (idx2 != -1) string = string.substring(0, idx2) + string + } + + + /** Creates a view of this iterable @see IterableView + */ + def view: IterableView[CC, A] = new IterableView[CC, A] { // !!! Martin: We should maybe infer the type parameters here? + val origin = thisCC + val elements: Iterator[A] = self.elements + } + +// The following methods return non-deterministic results, unless this iterable is an OrderedIterable + + /** The first element of this sequence. + * + * @note Might return different results for different runs, unless this iterable is ordered + * @throws Predef.NoSuchAentException if the sequence is empty. + */ + def head: A = if (isEmpty) throw new NoSuchElementException else elements.next + + /** @deprecated use head instead */ + @deprecated def first: A = head + + /** Returns as an option the first element of this iterable + * or None if iterable is empty. + * @note Might return different results for different runs, unless this iterable is ordered + */ + def headOption: Option[A] = if (isEmpty) None else Some(head) + + /** @deprecated use headOption instead + * None if list is empty. + */ + @deprecated def firstOption: Option[A] = headOption + + /** An iterable consisting of all elements of this iterable + * except the first one. + * @note Might return different results for different runs, unless this iterable is ordered + */ + def tail: CC[A] = drop(1) + + /** Return an iterable consisting only of the first n + * elements of this iterable, or else the whole iterable, if it has less + * than n elements. + * + * @param n the number of elements to take + * @return a possibly projected sequence + * @note Might return different results for different runs, unless this iterable is ordered + */ + def take(n: Int): CC[A] = { + val b = newBuilder[A] + var i = 0 + breakable { + for (x <- this) { + b += x + i += 1 + if (i == n) break + } + } + b.result + } + + /** Returns this iterable without its n first elements + * If this iterable has less than n elements, the empty + * iterable is returned. + * + * @param n the number of elements to drop + * @return the new iterable + * @note Might return different results for different runs, unless this iterable is ordered + */ + def drop(n: Int): CC[A] = { + val b = newBuilder[A] + var i = 0 + for (x <- this) { + if (i >= n) b += x + i += 1 + } + b.result + } + + /** A sub-iterable starting at index `from` + * and extending up to (but not including) index `until`. + * + * @note c.slice(from, to) is equivalent to (but possibly more efficient than) + * c.drop(from).take(to - from) + * + * @param from The index of the first element of the returned subsequence + * @param until The index of the element following the returned subsequence + * @throws IndexOutOfBoundsException if from < 0 + * or length < from + len + * @note Might return different results for different runs, unless this iterable is ordered + */ + def slice(from: Int, until: Int): CC[A] = { + val b = newBuilder[A] + var i = 0 + breakable { + for (x <- this) { + if (i >= from) b += x + i += 1 + if (i == until) break + } + } + b.result + } + + /** The last element of this iterable. + * + * @throws Predef.NoSuchElementException if the sequence is empty. + * @note Might return different results for different runs, unless this iterable is ordered + */ + def last: A = { + var lst = head + for (x <- this) + lst = x + lst + } + + /** Returns as an option the last element of this iterable or + * None if iterable is empty. + * + * @return the last element as an option. + * @note Might return different results for different runs, unless this iterable is ordered + */ + def lastOption: Option[A] = if (isEmpty) None else Some(last) + + /** An iterable consisting of all elements of this iterable except the last one. + * @note Might return different results for different runs, unless this iterable is ordered + */ + def init: CC[A] = { + var lst = head + val b = newBuilder[A] + for (x <- this) { + b += lst + lst = x + } + b.result + } + + /** Returns the rightmost n elements from this iterable. + * + * @param n the number of elements to take + * @note Might return different results for different runs, unless this iterable is ordered + */ + def takeRight(n: Int): CC[A] = { + val b = newBuilder[A] + val lead = elements drop n + var go = false + for (x <- this) { + if (go) b += x + else if (lead.hasNext) lead.next + else go = true + } + b.result + } + + /** Returns the iterable wihtout its rightmost n elements. + * + * @param n the number of elements to take + * @note Might return different results for different runs, unless this iterable is ordered + */ + def dropRight(n: Int): CC[A] = { + val b = newBuilder[A] + val lead = elements drop n + breakable { + for (x <- this) { + if (!lead.hasNext) break + lead.next + b += x + } + } + b.result + } + + /** Split the iterable at a given point and return the two parts thus + * created. + * + * @param n the position at which to split + * @return a pair of iterables composed of the first n + * elements, and the other elements. + * @note Might return different results for different runs, unless this iterable is ordered + */ + def splitAt(n: Int): (CC[A], CC[A]) = { + val l, r = newBuilder[A] + var i = 0 + for (x <- this) + (if (i < n) l else r) += x + (l.result, r.result) + } + + /** Returns the longest prefix of this sequence whose elements satisfy + * the predicate p. + * + * @param p the test predicate. + * @return the longest prefix of this sequence whose elements satisfy + * the predicate p. + * @note Might return different results for different runs, unless this iterable is ordered + */ + def takeWhile(p: A => Boolean): CC[A] = { + val b = newBuilder[A] + breakable { + for (x <- this) { + if (!p(x)) break + b += x + } + } + b.result + } + + /** Returns the longest suffix of this sequence whose first element + * does not satisfy the predicate p. + * + * @param p the test predicate. + * @return the longest suffix of the sequence whose first element + * does not satisfy the predicate p. + * @note Might return different results for different runs, unless this iterable is ordered + */ + def dropWhile(p: A => Boolean): CC[A] = { + val b = newBuilder[A] + var go = false + for (x <- this) { + if (go) b += x + else if (!p(x)) { go = true; b += x } + } + b.result + } + + /** Returns a pair consisting of the longest prefix of the list whose + * elements all satisfy the given predicate, and the rest of the list. + * + * @param p the test predicate + * @return a pair consisting of the longest prefix of the list whose + * elements all satisfy p, and the rest of the list. + * @note Might return different results for different runs, unless this iterable is ordered + */ + def span(p: A => Boolean): (CC[A], CC[A]) = { + val l, r = newBuilder[A] + var toLeft = true + for (x <- this) { + toLeft = toLeft && p(x) + (if (toLeft) l else r) += x + } + (l.result, r.result) + } + + /** Checks if the other iterable object contains the same elements as this one. + * + * @note will not terminate for infinite-sized iterables. + * @param that the other iterable + * @return true, iff both iterables contain the same elements. + * @note Might return different results for different runs, unless this iterable is ordered + */ + def sameElements[B >: A](that: OrderedIterable[B]): Boolean = { + val these = this.elements + val those = that.elements + while (these.hasNext && those.hasNext && these.next() == those.next()) {} + !these.hasNext && !those.hasNext + } + + /** A sub-sequence view starting at index `from` + * and extending up to (but not including) index `until`. + * + * @param from The index of the first element of the slice + * @param until The index of the element following the slice + * @note The difference between `view` and `slice` is that `view` produces + * a view of the current sequence, whereas `slice` produces a new sequence. + * + * @note Might return different results for different runs, unless this iterable is ordered + * @note view(from, to) is equivalent to view.slice(from, to) + */ + def view(from: Int, until: Int): IterableView[CC, A] = view.slice(from, until) +} diff --git a/src/library/scalax/collection/generic/covartest/IterableView.scala b/src/library/scalax/collection/generic/covartest/IterableView.scala new file mode 100755 index 0000000000..80a455f776 --- /dev/null +++ b/src/library/scalax/collection/generic/covartest/IterableView.scala @@ -0,0 +1,121 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Iterable.scala 15188 2008-05-24 15:01:02Z stepancheg $ + +package scalax.collection.generic.covartest + +/** A non-strict projection of an iterable. + * @author Sean McDirmid + * @author Martin Odersky + * @note this should really be a virtual class of SequenceFactory + */ +trait IterableView[+UC[+B] <: Iterable[B], +A] extends Iterable[A] { self => + + val origin: Iterable[_] + def elements: Iterator[A] + + val underlying: Iterable[_] = origin match { + case v: IterableView[t, _] => v.underlying + case _ => origin + } + + private def isDelay = elements eq underlying.elements + + private[this] var forced: UC[A] = _ + private[this] var wasForced = false + + def force: UC[A] = { + if (!wasForced) { + forced = { + val b = underlying.newBuilder[A] + for (x <- elements) + b += x + b.result + }.asInstanceOf[UC[A]] + wasForced = true + } + forced + } + + def newBuilder[A] = underlying.newBuilder[A] + + /** Builds a new view object. This method needs to be overridden in subclasses + * which refine in IterableView type + */ + protected def newView[B](elems: Iterator[B]) = new IterableView[UC, B] { + val origin = if (self.isDelay) self.origin else self + val elements = elems + } + + /** Non-strict variant of @see IterableLike.++ */ + override def ++[B >: A](that: Iterator[B]): IterableView[UC, B] = newView(elements ++ that) + + /** Non-strict variant of @see IterableLike.++ */ + override def ++[B >: A](that: Iterable[B]): IterableView[UC, B] = newView(elements ++ that.elements) + + /** Non-strict variant of @see IterableLike.map */ + override def map[B](f: A => B): IterableView[UC, B] = newView(elements map f) + + /** Non-strict variant of @see IterableLike.flatMap */ + override def flatMap[B](f: A => Iterable[B]): IterableView[UC, B] = newView(elements flatMap (f(_).elements)) + + /** Non-strict variant of @see IterableLike.filter */ + override def filter(p: A => Boolean): IterableView[UC, A] = newView(elements filter p) + + /** Non-strict variant of @see IterableLike.partition */ + override def partition(p: A => Boolean): (IterableView[UC, A], IterableView[UC, A]) = { + val (li, ri) = elements partition p + (newView(li), newView(ri)) + } + + /** Non-strict variant of @see IterableLike.zip */ + override def zip[B](other: Iterable[B]): IterableView[UC, (A, B)] = newView(elements zip other.elements) + + /** Non-strict variant of @see IterableLike.zipWithIndex */ + override def zipWithIndex: IterableView[UC, (A, Int)] = newView(elements.zipWithIndex) + + /* Non-strict variant of @see IterableLike.zipAll + * This is not enabled because it can't be specialized in VectorView: + * VectorView is not covariant, yet must maintain updatability. Impossible to do this + * with this type signature. + override def zipAll[B, A1 >: A, B1 >: B](that: Iterable[B], thisElem: A1, thatElem: B1): IterableView[UC, (A1, B1)] = + newView(elements zipAll (that.elements, thisElem, thatElem)) + */ + + /** Non-strict variant of @see Iterable.take */ + override def take(n: Int): IterableView[UC, A] = newView(elements take n) + + /** Non-strict variant of @see Iterable.drop */ + override def drop(n: Int): IterableView[UC, A] = newView(elements drop n) + + /** Non-strict variant of @see Iterable.splitAt */ + override def splitAt(n: Int): (IterableView[UC, A], IterableView[UC, A]) = (take(n), drop(n)) + + /** Non-strict variant of @see Iterable.slice */ + override def slice(from: Int, until: Int): IterableView[UC, A] = newView(elements slice (from, until)) + + /** Non-strict variant of @see Iterable.takeWhile */ + override def takeWhile(p: A => Boolean): IterableView[UC, A] = newView(elements takeWhile p) + + /** Non-strict variant of @see Iterable.dropWhile */ + override def dropWhile(p: A => Boolean): IterableView[UC, A] = newView(elements dropWhile p) + + /** Non-strict variant of @see Iterable.span */ + override def span(p: A => Boolean): (IterableView[UC, A], IterableView[UC, A]) = (takeWhile(p), dropWhile(p)) + + /** The projection resulting from the concatenation of this projection with the rest projection. + * @param rest The projection that gets appended to this projection + * @deprecated Use ++ instead + */ + @deprecated def append[B >: A](rest : => Iterable[B]): IterableView[UC, B] = this ++ rest.elements + + override def stringPrefix = origin.stringPrefix+"V" + + +} diff --git a/src/library/scalax/collection/generic/covartest/OrderedIterableTemplate.scala b/src/library/scalax/collection/generic/covartest/OrderedIterableTemplate.scala new file mode 100755 index 0000000000..c6be1a5acd --- /dev/null +++ b/src/library/scalax/collection/generic/covartest/OrderedIterableTemplate.scala @@ -0,0 +1,17 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Iterable.scala 15188 2008-05-24 15:01:02Z stepancheg $ + + +package scalax.collection.generic.covartest + +import OrderedIterable._ + +trait OrderedIterableTemplate[+CC[+B] <: OrderedIterableTemplate[CC, B] with OrderedIterable[B], +A] + extends IterableTemplate[CC, A] diff --git a/src/library/scalax/collection/generic/covartest/SequenceFactory.scala b/src/library/scalax/collection/generic/covartest/SequenceFactory.scala new file mode 100755 index 0000000000..1fb85d02db --- /dev/null +++ b/src/library/scalax/collection/generic/covartest/SequenceFactory.scala @@ -0,0 +1,11 @@ +package scalax.collection.generic.covartest + +trait SequenceFactory[CC[A] <: Sequence[A]] extends IterableFactory[CC] { + + /** This method is called in a pattern match { case Sequence(...) => }. + * + * @param x the selector value + * @return sequence wrapped in an option, if this is a Sequence, otherwise none + */ + def unapplySequence[A](x: CC[A]): Some[CC[A]] = Some(x) +} diff --git a/src/library/scalax/collection/generic/covartest/SequenceTemplate.scala b/src/library/scalax/collection/generic/covartest/SequenceTemplate.scala new file mode 100755 index 0000000000..bf1915edf0 --- /dev/null +++ b/src/library/scalax/collection/generic/covartest/SequenceTemplate.scala @@ -0,0 +1,382 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Sequence.scala 16092 2008-09-12 10:37:06Z nielsen $ + + +package scalax.collection.generic.covartest + +import util.control.Break._ +import scalax.collection.immutable.{List, Nil, ::} + +import Sequence._ + +trait SequenceTemplate[+CC[+B] <: SequenceTemplate[CC, B] with Sequence[B], +A] extends PartialFunction[Int, A] with OrderedIterableTemplate[CC, A] { +self /*: CC[A]*/ => + + /** Returns the length of the sequence. + * + * @return the sequence length. + */ + def length: Int + + /** Result of comparing length with operand len. + * returns x where + * x < 0 iff this.length < len + * x == 0 iff this.length == len + * x > 0 iff this.length > len. + * + * The method as implemented here does not call length directly; its running time + * is O(length min len) instead of O(length). The method should be overwritten + * if computing length is cheap. + */ + def lengthCompare(len: Int): Int = { + var i = 0 + breakable { + for (_ <- this) { + i += 1 + if (i > len) break + } + } + i - len + } + + /** Should always be length */ + def size = length + + /** Is this partial function defined for the index x? + */ + def isDefinedAt(x: Int): Boolean = (x >= 0) && (x < length) + + /** Returns index of the first element satisying a predicate, or -1, if none exists. + * + * @note may not terminate for infinite-sized collections. + * @param p the predicate + */ + def indexWhere(p: A => Boolean): Int = indexWhere(p, 0) + + /** Returns length of longest segment starting from a start index `from` + * such that every element of the segment satisfies predicate `p`. + * @note may not terminate for infinite-sized collections. + * @param p the predicate + * @param from the start index + */ + def segmentLength(p: A => Boolean, from: Int): Int = { + var result = 0 + var i = from + breakable { + for (x <- this) { + if (i >= from && !p(x)) { result = i - from; break } + else i += 1 + } + } + result + } + + /** Returns length of longest prefix of this seqence + * such that every element of the prefix satisfies predicate `p`. + * @note may not terminate for infinite-sized collections. + * @param p the predicate + */ + def prefixLength(p: A => Boolean) = segmentLength(p, 0) + + /** Returns index of the first element starting from a start index + * satisying a predicate, or -1, if none exists. + * + * @note may not terminate for infinite-sized collections. + * @param p the predicate + * @param from the start index + */ + def indexWhere(p: A => Boolean, from: Int): Int = { + var result = -1 + var i = from + breakable { + for (x <- this) { + if (i >= from && p(x)) { result = i; break } + else i += 1 + } + } + result + } + + /** Returns index of the first element satisying a predicate, or -1. + * + * @deprecated Use `indexWhere` instead + */ + @deprecated def findIndexOf(p: A => Boolean): Int = indexWhere(p) + + /** Returns the index of the first occurence of the specified + * object in this iterable object. + * + * @note may not terminate for infinite-sized collections. + * @param elem element to search for. + * @return the index in this sequence of the first occurence of the + * specified element, or -1 if the sequence does not contain + * this element. + */ + def indexOf[B >: A](elem: B): Int = indexOf(elem, 0) + + /** Returns the index of the first occurence of the specified + * object in this iterable object, starting from a start index, or + * -1, if none exists. + * + * @note may not terminate for infinite-sized collections. + * @param elem element to search for. + */ + def indexOf[B >: A](elem: B, from: Int): Int = indexWhere(elem ==, from) + + /** Returns the index of the last occurence of the specified element + * in this sequence, or -1 if the sequence does not contain this element. + * + * @param elem element to search for. + * @return the index in this sequence of the last occurence of the + * specified element, or -1 if the sequence does not contain + * this element. + */ + def lastIndexOf[B >: A](elem: B): Int = lastIndexOf(elem, length - 1) + + /** Returns the index of the last + * occurence of the specified element in this sequence + * before or at a given end index, + * or -1 if the sequence does not contain this element. + * + * @param elem element to search for. + * @param end the end index + */ + def lastIndexOf[B >: A](elem: B, end: Int): Int = { + var i = end + val it = reversedElements + while (it.hasNext && it.next != elem) i -= 1 + i + } + + /** Returns index of the last element satisying a predicate, or -1, if none exists. + * + * @param p the predicate + * @return the index of the last element satisfying p, + * or -1 if such an element does not exist + */ + def lastIndexWhere(p: A => Boolean): Int = lastIndexWhere(p, length - 1) + + /** Returns index of the last element not exceeding a given end index + * and satisying a predicate, or -1 if none exists. + * + * @param end the end index + * @param p the predicate + */ + def lastIndexWhere(p: A => Boolean, end: Int): Int = { + var i = end + val it = reversedElements + while (it.hasNext && (i > end || !p(it.next))) i -= 1 + i + } + + /** A sequence of type C consisting of all elements of + * this sequence in reverse order. + * @note the operation is implemented by building a new sequence + * from this(length - 1), ..., this(0) + * If random access is inefficient for the given sequence implementation, + * this operation should be overridden. + */ + def reverse: CC[A] = { + var xs: List[A] = List() + for (x <- this) + xs = x :: xs + val b = newBuilder[A] + for (x <- xs) + b += x + b.result + } + + /** The elements of this sequence in reversed order + */ + def reversedElements: Iterator[A] = reverse.elements + + /** + * Checks whether the argument sequence is contained at the + * specified index within the receiver object. + * + * If the both the receiver object, this and + * the argument, that are infinite sequences + * this method may not terminate. + * + * @return true if that is contained in + * this, at the specified index, otherwise false + * + * @see String.startsWith + */ + def startsWith[B](that: Sequence[B], offset: Int): Boolean = { + val i = elements.drop(offset) + val j = that.elements + while (j.hasNext && i.hasNext && i.next == j.next) {} + !j.hasNext + } + + /** + * Check whether the receiver object starts with the argument sequence. + * + * @return true if that is a prefix of this, + * otherwise false + * + * @see Sequence.startsWith + */ + def startsWith[B](that: Sequence[B]): Boolean = startsWith(that, 0) + + /** @return true if this sequence end with that sequence + * @see String.endsWith + */ + def endsWith[B](that: Sequence[B]): Boolean = { + val i = this.elements.drop(length - that.length) + val j = that.elements + while (i.hasNext && j.hasNext && i.next == j.next) () + !j.hasNext + } + + /** @return -1 if that not contained in this, otherwise the + * index where that is contained + * @see String.indexOf + */ + def indexOf[B >: A](that: Sequence[B]): Int = { + var i = 0 + var s: Sequence[A] = thisCC + while (!s.isEmpty && !(s startsWith that)) { + i += 1 + s = s drop 1 + } + if (!s.isEmpty || that.isEmpty) i else -1 + } + + /** Tests if the given value elem is a member of this + * sequence. + * + * @param elem element whose membership has to be tested. + * @return true iff there is an element of this sequence + * which is equal (w.r.t. ==) to elem. + */ + def contains(elem: Any): Boolean = exists (_ == elem) + + /** Computes the union of this sequence and the given sequence + * that. + * + * @param that the sequence of elements to add to the sequence. + * @return an sequence containing the elements of this + * sequence and those of the given sequence that + * which are not contained in this sequence. + */ + def union[B >: A](that: Sequence[B]): CC[B] = this ++ (that diff thisCC) + + /** Computes the difference between this sequence and the given sequence + * that. + * + * @param that the sequence of elements to remove from this sequence. + * @return this sequence without the elements of the given sequence + * that. + */ + def diff[B >: A](that: Sequence[B]): CC[A] = this remove (that contains _) + + /** Computes the intersection between this sequence and the given sequence + * that. + * + * @param that the sequence to intersect. + * @return the sequence of elements contained both in this sequence and + * in the given sequence that. + */ + def intersect[B >: A](that: Sequence[B]): CC[A] = this filter(that contains _) + + /** Builds a new sequence from this sequence in which any duplicates (wrt to ==) removed. + * Among duplicate elements, only the first one is retained in the result sequence + */ + def removeDuplicates: CC[A] = { + val b = newBuilder[A] + var seen = Set[A]() + for (x <- this) { + if (!(seen contains x)) { + b += x + seen += x + } + } + b.result + } + + /** Returns a sequence of given length containing the elements of this sequence followed by zero + * or more occurrences of given elements. If this sequence is already at least as long as given + * length, it is returned directly. Otherwise, a new sequence is created consisting of the elements + * of this sequence followed by enough occurrences of the given elements to reach the given length. + */ + def padTo[B >: A](len: Int, elem: B): CC[B] = { + var diff = len - length + val b = newBuilder[B] //!!! drop [B] and get surprising results! + b ++= thisCC + while (diff > 0) { + b += elem + diff -=1 + } + b.result + } + + /** + * Overridden for efficiency. + * + * @return the sequence itself + */ + override def toSequence: Sequence[A] = this.asInstanceOf[Null] // !!! + + def indices: Range = 0 until length + + /** Creates a view of this iterable @see OrderedIterable.View + */ + override def view: SequenceView[CC, A] = new SequenceView[CC, A] { // !!! Martin: We should maybe infer the type parameters here? + val origin: Sequence[_] = thisCC + val elements: Iterator[A] = self.elements + val length: Int = self.length + def apply(idx: Int): A = self.apply(idx) + } + + /** A sub-sequence view starting at index `from` + * and extending up to (but not including) index `until`. + * + * @param from The index of the first element of the slice + * @param until The index of the element following the slice + * @note The difference between `view` and `slice` is that `view` produces + * a view of the current sequence, whereas `slice` produces a new sequence. + * + * @note view(from, to) is equivalent to view.slice(from, to) + */ + override def view(from: Int, until: Int): SequenceView[CC, A] = view.slice(from, until) + + /** Returns index of the last element satisying a predicate, or -1. + * @deprecated use `lastIndexWhere` instead + */ + @deprecated def findLastIndexOf(p: A => Boolean): Int = lastIndexWhere(p) + + /** A sub-sequence starting at index from + * and extending up to the length of the current sequence + * + * @param from The index of the first element of the slice + * @throws IndexOutOfBoundsException if from < 0 + * @deprecated use drop instead + */ + @deprecated def slice(from: Int): Sequence[A] = slice(from, length) + + /** @deprecated Should be replaced by + * + * (s1, s2) forall { case (x, y) => f(x, y) } + */ + @deprecated def equalsWith[B](that: Sequence[B])(f: (A,B) => Boolean): Boolean = { + val i = this.elements + val j = that.elements + while (i.hasNext && j.hasNext && f(i.next, j.next)) () + !i.hasNext && !j.hasNext + } + + /** Is that a slice in this? + * @deprecated Should be repaced by indexOf(that) != -1 + */ + @deprecated def containsSlice[B](that: Sequence[B]): Boolean = indexOf(that) != -1 + +} diff --git a/src/library/scalax/collection/generic/covartest/SequenceView.scala b/src/library/scalax/collection/generic/covartest/SequenceView.scala new file mode 100755 index 0000000000..48477cf6e7 --- /dev/null +++ b/src/library/scalax/collection/generic/covartest/SequenceView.scala @@ -0,0 +1,129 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Sequence.scala 16092 2008-09-12 10:37:06Z nielsen $ + + +package scalax.collection.generic.covartest + +import util.control.Break._ +import annotation.unchecked.uncheckedVariance + +import Sequence._ + +/** A non-strict projection of an iterable. + * @author Sean McDirmid + * @author Martin Odersky + * @note this should really be a virtual class of SequenceFactory + */ +trait SequenceView[+UC[+B] <: Sequence[B], +A] extends IterableView[UC, A] with Sequence[A] { +self => + + /** refined from Iterable.View */ + val origin: Sequence[_] + + trait Transformed[+B] extends SequenceView[UC, B] { + val origin = self + protected def asCC = asInstanceOf[SequenceView[UC, B]] + } + + class Appended[+B >: A](that: Sequence[B]) extends Transformed[B] { + override def elements: Iterator[B] = self.elements ++ that.elements + override def length: Int = self.length + that.length + override def apply(idx: Int): B = { + val ll = self.length + if (idx < ll) self.apply(idx) else that.apply(idx - ll) + } + override def stringPrefix = self.stringPrefix + "A" + } + + class Sliced(from: Int, to: Int) extends Transformed[A] { + override def elements: Iterator[A] = self.elements slice (from, to) + override lazy val length: Int = ((to min self.length) - (from max 0) min 0) + override def apply(idx: Int): A = + if (idx >= 0 && idx < length) self.apply(idx + from) + else throw new IndexOutOfBoundsException(idx.toString) + override def stringPrefix = self.stringPrefix + "S" + override def slice(from1: Int, to1: Int) = + new self.Sliced(from + (from1 min length max 0) , to + (to1 min length max 0)).asInstanceOf[SequenceView[UC, A]] + } + + class Mapped[+B](f: A => B) extends Transformed[B] { + override def elements: Iterator[B] = self.elements map f + override def length: Int = self.length + override def apply(idx: Int): B = f(self.apply(idx)) + override def stringPrefix = self.stringPrefix + "M" + } + + class Reversed extends Transformed[A] { + override def elements: Iterator[A] = self.reversedElements + override def length: Int = self.length + override def apply(idx: Int): A = self.apply(length - 1 - idx) + override def stringPrefix = super.stringPrefix+"R" + } + + class Patched[+B >: A](from: Int, patch: Sequence[B], replaced: Int) extends Transformed[B] { + val plen = patch.length + override def elements: Iterator[B] = self.elements patch (from, patch.asInstanceOf[Null], replaced) // !!! + override def length: Int = self.length + plen - replaced + override def apply(idx: Int): B = + if (idx < from) self.apply(idx) + else if (idx < from + plen) patch.apply(idx - from) + else self.apply(idx - plen + replaced) + override def stringPrefix = super.stringPrefix+"P" + } + + class Zipped[+B](that: Sequence[B]) extends Transformed[(A, B)] { + override def elements: Iterator[(A, B)] = self.elements zip that.elements + override def length = self.length min that.length + override def apply(idx: Int): (A, B) = (self.apply(idx), that.apply(idx)) + override def stringPrefix = super.stringPrefix+"Z" + } + + override def ++[B >: A](that: Iterable[B]): SequenceView[UC, B] = + new Appended[B](that.toSequence).asCC + override def ++[B >: A](that: Iterator[B]): SequenceView[UC, B] = + new Appended[B](that.toSequence).asCC + override def map[B](f: A => B): SequenceView[UC, B] = + new Mapped(f).asCC + override def reverse: SequenceView[UC, A] = + (new Reversed).asCC + def patch[B >: A](from: Int, patch: Sequence[B], replaced: Int): SequenceView[UC, B] = + if (0 <= from && from < length) new Patched(from, patch, replaced).asCC + else throw new IndexOutOfBoundsException(from.toString) + override def padTo[B >: A](len: Int, elem: B): SequenceView[UC, B] = + patch(length, fill((len - length) max 0, elem), 0) + override def zip[B](that: Iterable[B]): SequenceView[UC, (A, B)] = + new Zipped(that.toSequence).asCC + override def zipWithIndex: SequenceView[UC, (A, Int)] = + zip((0 until length).asInstanceOf[Null]) // !!! + /* + override def zipAll[B, A1 >: A, B1 >: B](that: Iterable[B], thisElem: A1, thatElem: B1): SequenceView[UC, (A1, B1)] = { + val that1 = that.toSequence + self.padTo(that1.length, thisElem) zip that1.padTo(this.length, thatElem)//.asInstanceOf[SequenceView[UC, (A1, B1)]] + }*/ + override def take(n: Int): SequenceView[UC, A] = + slice(0, n) + override def drop(n: Int): SequenceView[UC, A] = + slice(n, Math.MAX_INT) + override def splitAt(n: Int): (SequenceView[UC, A], SequenceView[UC, A]) = (take(n), drop(n)) + override def slice(from: Int, until: Int): SequenceView[UC, A] = + new Sliced(from, until).asCC + override def takeWhile(p: A => Boolean): SequenceView[UC, A] = + take(prefixLength(p)) + override def dropWhile(p: A => Boolean): SequenceView[UC, A] = + drop(prefixLength(p)) + override def span(p: A => Boolean): (SequenceView[UC, A], SequenceView[UC, A]) = { + val n = prefixLength(p) + (take(n), drop(n)) + } + + // missing here because we can't do anything about them, so we fall back to default construction + // if an IterableView via newView: flatMap, filter, partition +} + diff --git a/src/library/scalax/collection/generic/covartest/VectorTemplate.scala b/src/library/scalax/collection/generic/covartest/VectorTemplate.scala new file mode 100644 index 0000000000..0771b078d9 --- /dev/null +++ b/src/library/scalax/collection/generic/covartest/VectorTemplate.scala @@ -0,0 +1,264 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2006-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Vector.scala 15437 2008-06-25 16:22:45Z stepancheg $ + +package scalax.collection.generic.covartest + +import Vector._ + +/** Sequences that support O(1) element access and O(1) length computation. + * @author Sean McDirmid + * @author Martin Odersky + * @version 2.8 + */ +trait VectorTemplate[+CC[+B] <: VectorTemplate[CC, B] with Vector[B], +A] extends SequenceTemplate[CC, A] { +self => + + // Overridden methods from IterableTemplate + + /** The iterator returned by the elements method + */ + protected class Elements(start: Int, end: Int) extends scalax.collection.BufferedIterator[A] { + var i = start + def hasNext: Boolean = i < end + def next: A = + if (i < end) { + val x = self(i) + i += 1 + x + } else Iterator.empty.next + def head = + self(i) + /** drop is overridden to enable fast searching in the middle of random access sequences */ + override def drop(n: Int): Iterator[A] = + if (n > 0) new Elements(start + n, end) else this + /** is overridden to be symmetric to drop */ + override def take(n: Int): Iterator[A] = + if (n < 0) Iterator.empty.buffered + else if (start + n < end) new Elements(start, start + n) + else this + } + + override def elements: Iterator[A] = new Elements(0, length) + + override def isEmpty: Boolean = { length == 0 } + + override def foreach(f: A => Unit): Unit = { + var i = 0 + val len = length + while (i < len) { f(this(i)); i += 1 } + } + + override def forall(p: A => Boolean): Boolean = prefixLength(p(_)) == length + override def exists(p: A => Boolean): Boolean = prefixLength(!p(_)) != length + + override def find(p: A => Boolean): Option[A] = { + val i = prefixLength(!p(_)) + if (i < length) Some(this(i)) else None + } + + private def foldl[B](start: Int, z: B, op: (B, A) => B): B = { + var i = start + val len = length + var result = z + while (i < len) { + result = op(result, this(i)) + i += 1 + } + result + } + + private def foldr[B](start: Int, len: Int, z: B, op: (A, B) => B): B = + if (start == len) z + else op(this(start), foldr(start + 1, len, z, op)) + + override def foldLeft[B](z: B)(op: (B, A) => B): B = foldl(0, z, op) + override def foldRight[B](z: B)(op: (A, B) => B): B = foldr(0, length, z, op) + override def reduceLeft[B >: A](op: (B, A) => B): B = + if (length > 0) foldl(0, this(0), op) else super.reduceLeft(op) + override def reduceRight[B >: A](op: (A, B) => B): B = + if (length > 0) foldr(0, length - 1, this(length - 1), op) else super.reduceRight(op) + + override def zip[B](that: Iterable[B]): CC[(A, B)] = that match { + case that: Vector[_] => + var i = 0 + val len = this.length min that.length + val b = this.newBuilder[(A, B)] + while (i < len) { + b += ((this(i), that(i).asInstanceOf[B])) + i += 1 + } + b.result + case _ => + super.zip(that) + } + + override def zipAll[B, A1 >: A, B1 >: B](that: Iterable[B], thisElem: A1, thatElem: B1): CC[(A1, B1)] = that match { + case that: Vector[_] => + var i = 0 + val len = this.length min that.length + val b = this.newBuilder[(A1, B1)] + while (i < len) { + b += ((this(i), that(i).asInstanceOf[B])) + i += 1 + } + while (i < this.length) { + b += ((this(i), thatElem)) + i += 1 + } + while (i < that.length) { + b += ((this(i), thatElem.asInstanceOf[B])) + i += 1 + } + b.result + case _ => + super.zipAll(that, thisElem, thatElem) + } + + override def zipWithIndex: CC[(A, Int)] = { + val b = newBuilder[(A, Int)] + var i = 0 + val len = length + while (i < len) { + b += ((this(i), i)) + i += 1 + } + b.result + } + + override def copyToArray[B >: A](xs: Array[B], start: Int, len: Int) { + var i = 0 + var j = start + val end = length min len min (xs.length - start) + while (i < end) { + xs(j) = this(i) + i += 1 + j += 1 + } + } + + // Overridden methods from OrderedIterableTemplate + + override def head: A = if (isEmpty) throw new NoSuchElementException else this(0) + + override def slice(from: Int, until: Int): CC[A] = { + val b = newBuilder[A] + var i = from max 0 + val end = until min length + while (i < end) { + b += this(i) + i += 1 + } + b.result + } + + override def take(n: Int): CC[A] = slice(0, n) + override def drop(n: Int): CC[A] = slice(n, length) + override def takeRight(n: Int): CC[A] = slice(length - n, length) + override def dropRight(n: Int): CC[A] = slice(0, length - n) + override def splitAt(n: Int): (CC[A], CC[A]) = (take(n), drop(n)) + override def takeWhile(p: A => Boolean): CC[A] = take(prefixLength(p)) + override def dropWhile(p: A => Boolean): CC[A] = drop(prefixLength(p)) + override def span(p: A => Boolean): (CC[A], CC[A]) = splitAt(prefixLength(p)) + override def last: A = if (length > 0) this(length - 1) else super.last + override def init: CC[A] = if (length > 0) slice(0, length - 1) else super.init + + override def sameElements[B >: A](that: OrderedIterable[B]): Boolean = that match { + case that: Vector[_] => + val len = length + len == that.length && { + var i = 0 + while (i < len && this(i) == that(i)) i += 1 + i == len + } + case _ => + super.sameElements(that) + } + + // Overridden methods from Sequence + + override def lengthCompare(len: Int): Int = length - len + + private def negLength(n: Int) = if (n == length) -1 else n + + override def indexWhere(p: A => Boolean, from: Int): Int = + negLength(from + segmentLength(!p(_), from)) + + override def lastIndexWhere(p: A => Boolean, end: Int): Int = { + var i = end + while (i >= 0 && !p(this(i))) i -= 1 + i + } + + override def lastIndexOf[B >: A](elem: B, end: Int): Int = lastIndexWhere(elem ==, end) + + override def reverse: CC[A] = { + val b = newBuilder[A] + var i = length + while (0 < i) { + i -= 1 + b += this(i) + } + b.result + } + + override def reversedElements = new Iterator[A] { + var i = length + def hasNext: Boolean = 0 < i + def next: A = + if (0 < i) { + i -= 1 + self(i) + } else Iterator.empty.next + } + + override def startsWith[B](that: Sequence[B], offset: Int): Boolean = that match { + case that: Vector[_] => + var i = offset + var j = 0 + val thisLen = length + val thatLen = that.length + while (i < thisLen && j < thatLen && this(i) == that(j)) { + i += 1 + j += 1 + } + j == thatLen + case _ => + var i = offset + val thisLen = length + val thatElems = that.elements + while (i < thisLen && thatElems.hasNext && this(i) == thatElems.next) { + i += 1 + } + !thatElems.hasNext + } + + override def endsWith[B](that: Sequence[B]): Boolean = that match { + case that: Vector[_] => + val thisLen = length + val thatLen = that.length + var i = thisLen - 1 + var j = thatLen - 1 + while (i >= 0 && j >= 0 && this(i) == that(j)) { + i -= 1 + j -= 1 + } + j == 0 + case _ => + super.endsWith(that) + } + + override def indexOf[B >: A](that: Sequence[B]): Int = { + var i = 0 + val last = length - that.length + while (i <= last && !startsWith(that, i)) i += 1 + negLength(i) + } +} + diff --git a/src/library/scalax/collection/generic/mutable/VectorTemplate.scala b/src/library/scalax/collection/generic/mutable/VectorTemplate.scala new file mode 100644 index 0000000000..aafee3ae3b --- /dev/null +++ b/src/library/scalax/collection/generic/mutable/VectorTemplate.scala @@ -0,0 +1,55 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2006-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Vector.scala 15437 2008-06-25 16:22:45Z stepancheg $ + +package scalax.collection.generic.mutable + +import collection.mutable.Vector +import collection.mutable.Vector._ + +/** Sequences that support O(1) element access and O(1) length computation. + * @author Sean McDirmid + * @author Martin Odersky + * @version 2.8 + */ +trait VectorTemplate[+CC[B] <: VectorTemplate[CC, B] with Vector[B], A] extends nonvariant.VectorTemplate[CC, A] { +self => + + def update(idx: Int, elem: A) + + /** Creates a view of this iterable @see OrderedIterable.View + */ + override def view: VectorView[CC, A] = new VectorView[CC, A] { // !!! Martin: We should maybe infer the type parameters here? + val origin: Vector[_] = thisCC + val length: Int = self.length + def apply(idx: Int): A = self.apply(idx) + def update(idx: Int, elem: A) = self.update(idx, elem) + } + + /** A sub-sequence view starting at index `from` + * and extending up to (but not including) index `until`. + * + * @param from The index of the first element of the slice + * @param until The index of the element following the slice + * @note The difference between `view` and `slice` is that `view` produces + * a view of the current sequence, whereas `slice` produces a new sequence. + * + * @note view(from, to) is equivalent to view.slice(from, to) + */ + override def view(from: Int, until: Int): VectorView[CC, A] = view.slice(from, until) + + def readOnly: Sequence[A] = new collection./*immutable.*/Vector[A] { //!!! + def length = self.length + def apply(idx : Int) = self.apply(idx) + def newBuilder[B]: Builder[CC, B] = self.newBuilder[B] + override def foreach(f: A => Unit) = self.foreach(f) + override def stringPrefix = self.stringPrefix+"RO" + } +} + diff --git a/src/library/scalax/collection/generic/mutable/VectorView.scala b/src/library/scalax/collection/generic/mutable/VectorView.scala new file mode 100644 index 0000000000..05a7bf6f5a --- /dev/null +++ b/src/library/scalax/collection/generic/mutable/VectorView.scala @@ -0,0 +1,95 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Sequence.scala 16092 2008-09-12 10:37:06Z nielsen $ + + +package scalax.collection.generic.mutable + +import collection.mutable.Vector +import collection.mutable.Vector._ + +/** A non-strict projection of an iterable. + * @author Sean McDirmid + * @author Martin Odersky + * @note this should really be a virtual class of SequenceFactory + */ +trait VectorView[+UC[B] <: Vector[B], A] extends SequenceView[UC, A] with Vector[A] { +self => + + /** refined from Iterable.View */ + val origin: Vector[_] + + trait Transformed[B] extends super.Transformed[B] with VectorView[UC, B] { + override val origin = self + override def elements: Iterator[B] = new Elements(0, length) + override protected def asCC = asInstanceOf[VectorView[UC, B]] + } + + class Appended(that: Vector[A]) extends super.Appended[A](that) with Transformed[A] { + override def update(idx: Int, elem: A) { + val ll = self.length + if (idx < ll) self.update(idx, elem) else that.update(idx - ll, elem) + } + } + + class Sliced(from: Int, to: Int) extends super.Sliced(from, to) with Transformed[A] { + override def update(idx: Int, elem: A) { + if (idx >= 0 && idx < length) self.update(idx + from, elem) + else throw new IndexOutOfBoundsException(idx.toString) + } + override def slice(from1: Int, to1: Int) = + new self.Sliced(from + (from1 min length max 0) , to + (to1 min length max 0)) + } + + class Reversed extends super.Reversed with Transformed[A] { + override def update(idx: Int, elem: A) { + self.update(length - 1 - idx, elem) + } + } + + class Zipped[B](that: Vector[B]) extends super.Zipped[B](that) with Transformed[(A, B)] { + override def update(idx: Int, elem: (A, B)) { + self.update(idx, elem._1) + that.update(idx, elem._2) + } + } + + def ++(that: Vector[A]): VectorView[UC, A] = + new Appended(that).asCC + + override def reverse: VectorView[UC, A] = + (new Reversed).asCC + + private def toVector[B](xs: Iterable[B]): Vector[B] = xs match { + case ras: Vector[_] => ras.asInstanceOf[Vector[B]] + case _ => Vector() ++ xs + } + + override def zip[B](that: Iterable[B]): VectorView[UC, (A, B)] = + new Zipped(toVector(that)).asCC + + override def zipWithIndex: VectorView[UC, (A, Int)] = + zip((0 until length).asInstanceOf[Null]) // !!! + override def take(n: Int): VectorView[UC, A] = + slice(0, n) + override def drop(n: Int): VectorView[UC, A] = + slice(n, Math.MAX_INT) + override def splitAt(n: Int): (VectorView[UC, A], VectorView[UC, A]) = (take(n), drop(n)) + override def slice(from: Int, until: Int): VectorView[UC, A] = + new Sliced(from, until).asCC + override def takeWhile(p: A => Boolean): VectorView[UC, A] = + take(prefixLength(p)) + override def dropWhile(p: A => Boolean): VectorView[UC, A] = + drop(prefixLength(p)) + override def span(p: A => Boolean): (VectorView[UC, A], VectorView[UC, A]) = { + val n = prefixLength(p) + (take(n), drop(n)) + } +} + diff --git a/src/library/scalax/collection/generic/nonvariant/IterableFactory.scala b/src/library/scalax/collection/generic/nonvariant/IterableFactory.scala new file mode 100755 index 0000000000..a3786a246d --- /dev/null +++ b/src/library/scalax/collection/generic/nonvariant/IterableFactory.scala @@ -0,0 +1,3 @@ +package scalax.collection.generic.nonvariant + +trait IterableFactory[CC[A] <: Iterable[A]] extends generic.IterableFactory[CC] diff --git a/src/library/scalax/collection/generic/nonvariant/IterableTemplate.scala b/src/library/scalax/collection/generic/nonvariant/IterableTemplate.scala new file mode 100755 index 0000000000..cb4b5bdaac --- /dev/null +++ b/src/library/scalax/collection/generic/nonvariant/IterableTemplate.scala @@ -0,0 +1,24 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Iterable.scala 15188 2008-05-24 15:01:02Z stepancheg $ + + +package scalax.collection.generic.nonvariant + +/** Collection classes mixing in this class provide a method + * elements which returns an iterator over all the + * elements contained in the collection. + * + * @note If a collection has a known size, it should also sub-type Collection. + * Only potentially unbounded collections should directly sub-class Iterable. + * @author Matthias Zenger + * @version 1.1, 04/02/2004 + */ +trait IterableTemplate[+CC[B] <: IterableTemplate[CC, B] with Iterable[B] , A] + extends generic.IterableTemplate[CC, A] { self /*: CC[A]*/ => } diff --git a/src/library/scalax/collection/generic/nonvariant/IterableView.scala b/src/library/scalax/collection/generic/nonvariant/IterableView.scala new file mode 100755 index 0000000000..66238b19ac --- /dev/null +++ b/src/library/scalax/collection/generic/nonvariant/IterableView.scala @@ -0,0 +1,19 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Iterable.scala 15188 2008-05-24 15:01:02Z stepancheg $ + +package scalax.collection.generic.nonvariant + +/** A non-strict projection of an iterable. + * @author Sean McDirmid + * @author Martin Odersky + * @note this should really be a virtual class of SequenceFactory + */ +trait IterableView[+UC[B] <: Iterable[B], A] + extends generic.IterableView[UC, A] diff --git a/src/library/scalax/collection/generic/nonvariant/OrderedIterableTemplate.scala b/src/library/scalax/collection/generic/nonvariant/OrderedIterableTemplate.scala new file mode 100755 index 0000000000..59358875a0 --- /dev/null +++ b/src/library/scalax/collection/generic/nonvariant/OrderedIterableTemplate.scala @@ -0,0 +1,15 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Iterable.scala 15188 2008-05-24 15:01:02Z stepancheg $ + + +package scalax.collection.generic.nonvariant + +trait OrderedIterableTemplate[+CC[B] <: OrderedIterableTemplate[CC, B] with OrderedIterable[B], A] + extends generic.OrderedIterableTemplate[CC, A] {self /*: CC[A]*/ => } diff --git a/src/library/scalax/collection/generic/nonvariant/SequenceFactory.scala b/src/library/scalax/collection/generic/nonvariant/SequenceFactory.scala new file mode 100755 index 0000000000..5c658b0bfb --- /dev/null +++ b/src/library/scalax/collection/generic/nonvariant/SequenceFactory.scala @@ -0,0 +1,3 @@ +package scalax.collection.generic.nonvariant + +trait SequenceFactory[CC[A] <: Sequence[A]] extends IterableFactory[CC] with generic.SequenceFactory[CC] diff --git a/src/library/scalax/collection/generic/nonvariant/SequenceTemplate.scala b/src/library/scalax/collection/generic/nonvariant/SequenceTemplate.scala new file mode 100755 index 0000000000..7f9b2acd60 --- /dev/null +++ b/src/library/scalax/collection/generic/nonvariant/SequenceTemplate.scala @@ -0,0 +1,15 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Sequence.scala 16092 2008-09-12 10:37:06Z nielsen $ + + +package scalax.collection.generic.nonvariant + +trait SequenceTemplate[+CC[B] <: SequenceTemplate[CC, B] with Sequence[B], A] + extends generic.SequenceTemplate[CC, A] {self /*: CC[A]*/ => } diff --git a/src/library/scalax/collection/generic/nonvariant/SequenceView.scala b/src/library/scalax/collection/generic/nonvariant/SequenceView.scala new file mode 100755 index 0000000000..46f5716ad4 --- /dev/null +++ b/src/library/scalax/collection/generic/nonvariant/SequenceView.scala @@ -0,0 +1,21 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Sequence.scala 16092 2008-09-12 10:37:06Z nielsen $ + + +package scalax.collection.generic.nonvariant + + +/** A non-strict projection of an iterable. + * @author Sean McDirmid + * @author Martin Odersky + * @note this should really be a virtual class of SequenceFactory + */ +trait SequenceView[+UC[B] <: Sequence[B], A] + extends IterableView[UC, A] with generic.SequenceView[UC, A] diff --git a/src/library/scalax/collection/generic/nonvariant/VectorTemplate.scala b/src/library/scalax/collection/generic/nonvariant/VectorTemplate.scala new file mode 100644 index 0000000000..7dfc4db64d --- /dev/null +++ b/src/library/scalax/collection/generic/nonvariant/VectorTemplate.scala @@ -0,0 +1,15 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Sequence.scala 16092 2008-09-12 10:37:06Z nielsen $ + + +package scalax.collection.generic.nonvariant + +trait VectorTemplate[+CC[B] <: VectorTemplate[CC, B] with Vector[B], A] + extends generic.VectorTemplate[CC, A] {self /*: CC[A]*/ => } diff --git a/src/library/scalax/collection/immutable/List.scala b/src/library/scalax/collection/immutable/List.scala new file mode 100644 index 0000000000..366ae15d15 --- /dev/null +++ b/src/library/scalax/collection/immutable/List.scala @@ -0,0 +1,1222 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: List.scala 16287 2008-10-18 13:41:36Z nielsen $ + + +package scalax.collection.immutable + +import mutable.ListBuffer +import generic.covariant.SequenceTemplate + +/** A class representing an ordered collection of elements of type + * a. This class comes with two implementing case + * classes scala.Nil and scala.:: that + * implement the abstract members isEmpty, + * head and tail. + * + * @author Martin Odersky and others + * @version 1.0, 16/07/2003 + */ +sealed abstract class List[+A] extends Sequence[A] with SequenceTemplate[List, A] with Product { + + /** Returns true if the list does not contain any elements. + * @return true, iff the list is empty. + */ + def isEmpty: Boolean + + /** Returns this first element of the list. + * + * @return the first element of this list. + * @throws Predef.NoSuchElementException if the list is empty. + */ + def head: A + + /** Returns this list without its first element. + * + * @return this list without its first element. + * @throws Predef.NoSuchElementException if the list is empty. + */ + def tail: List[A] + + def newBuilder[B]: Builder[List, B] = new ListBuffer[B] + + /**

+ * Add an element x at the beginning of this list. + *

+ * + * @param x the element to prepend. + * @return the list with x added at the beginning. + * @ex 1 :: List(2, 3) = List(2, 3).::(1) = List(1, 2, 3) + */ + def ::[B >: A] (x: B): List[B] = + new scalax.collection.immutable.::(x, this) + + /**

+ * Returns a list resulting from the concatenation of the given + * list prefix and this list. + *

+ * + * @param prefix the list to concatenate at the beginning of this list. + * @return the concatenation of the two lists. + * @ex List(1, 2) ::: List(3, 4) = List(3, 4).:::(List(1, 2)) = List(1, 2, 3, 4) + */ + def :::[B >: A](prefix: List[B]): List[B] = + if (isEmpty) prefix + else (new ListBuffer[B] ++ prefix).prependToList(this) + + /** Appends two list objects. + */ + override def ++[B >: A](that: Iterable[B]): List[B] = + this ::: that.toList + + /** Reverse the given prefix and append the current list to that. + * This function is equivalent to an application of reverse + * on the prefix followed by a call to :::, but more + * efficient (and tail recursive). + * + * @param prefix the prefix to reverse and then prepend + * @return the concatenation of the reversed prefix and the current list. + */ + def reverse_:::[B >: A](prefix: List[B]): List[B] = { + var these: List[B] = this + var pres = prefix + while (!pres.isEmpty) { + these = pres.head :: these + pres = pres.tail + } + these + } + + /** Returns the number of elements in the list. + * + * @return the number of elements in the list. + */ + def length: Int = { + var these = this + var len = 0 + while (!these.isEmpty) { + len += 1 + these = these.tail + } + len + } + + /** Returns the elements in the list as an iterator + * + * @return an iterator on the list elements. + */ + override def elements: Iterator[A] = new Iterator[A] { + var these = List.this + def hasNext: Boolean = !these.isEmpty + def next: A = + if (!hasNext) + throw new NoSuchElementException("next on empty Iterator") + else { + val result = these.head; these = these.tail; result + } + override def toList: List[A] = these + } + + /** Overrides the method in Iterable for efficiency. + * + * @return the list itself + */ + override def toList: List[A] = this + + /** Returns the n first elements of this list, or else the whole + * list, if it has less than n elements. + * + * @param n the number of elements to take. + * @return the n first elements of this list. + */ + override def take(n: Int): List[A] = { + val b = new ListBuffer[A] + var i = 0 + var these = this + while (!these.isEmpty && i < n) { + i += 1 + b += these.head + these = these.tail + } + if (these.isEmpty) this + else b.toList + } + + /** Returns the list with elements belonging to the given index range. + * + * @param start the start position of the list slice. + * @param end the end position (exclusive) of the list slice. + * @return the list with elements belonging to the given index range. + */ + override def slice(start: Int, end: Int): List[A] = { + val s = start max 0 + val e = end min this.length + drop(s) take (e - s) + } + + /** Returns the list without its n first elements. + * If this list has less than n elements, the empty list is returned. + * + * @param n the number of elements to drop. + * @return the list without its n first elements. + */ + override def drop(n: Int): List[A] = { + var these = this + var count = n + while (!these.isEmpty && count > 0) { + these = these.tail + count -= 1 + } + these + } + + /** Returns the rightmost n elements from this list. + * + * @param n the number of elements to take + * @return the suffix of length n of the list + */ + override def takeRight(n: Int): List[A] = { + def loop(lead: List[A], lag: List[A]): List[A] = lead match { + case Nil => lag + case _ :: tail => loop(tail, lag.tail) + } + loop(drop(n), this) + } + + /** Returns the list wihout its rightmost n elements. + * + * @param n the number of elements to take + * @return the list without its rightmost n elements + */ + override def dropRight(n: Int): List[A] = { + def loop(lead: List[A], lag: List[A]): List[A] = lead match { + case Nil => Nil + case _ :: tail => lag.head :: loop(tail, lag.tail) + } + loop(drop(n), this) + } + + /** Split the list at a given point and return the two parts thus + * created. + * + * @param n the position at which to split + * @return a pair of lists composed of the first n + * elements, and the other elements. + */ + override def splitAt(n: Int): (List[A], List[A]) = { + val b = new ListBuffer[A] + var i = 0 + var these = this + while (!these.isEmpty && i < n) { + i += 1 + b += these.head + these = these.tail + } + (b.toList, these) + } + + /** Returns the longest prefix of this list whose elements satisfy + * the predicate p. + * + * @param p the test predicate. + * @return the longest prefix of this list whose elements satisfy + * the predicate p. + */ + override def takeWhile(p: A => Boolean): List[A] = { + val b = new ListBuffer[A] + var these = this + while (!these.isEmpty && p(these.head)) { + b += these.head + these = these.tail + } + b.toList + } + + /** Returns the longest suffix of this list whose first element + * does not satisfy the predicate p. + * + * @param p the test predicate. + * @return the longest suffix of the list whose first element + * does not satisfy the predicate p. + */ + override def dropWhile(p: A => Boolean): List[A] = + if (isEmpty || !p(head)) this + else tail dropWhile p + + /** Returns the longest prefix of the list whose elements all satisfy + * the given predicate, and the rest of the list. + * + * @param p the test predicate + * @return a pair consisting of the longest prefix of the list whose + * elements all satisfy p, and the rest of the list. + */ + override def span(p: A => Boolean): (List[A], List[A]) = { + val b = new ListBuffer[A] + var these = this + while (!these.isEmpty && p(these.head)) { + b += these.head + these = these.tail + } + (b.toList, these) + } + + /** Returns the n-th element of this list. The first element + * (head of the list) is at position 0. + * + * @param n index of the element to return + * @return the element at position n in this list. + * @throws Predef.NoSuchElementException if the list is too short. + */ + def apply(n: Int): A = drop(n).head + + /** Returns the list resulting from applying the given function f to each + * element of this list. + * + * @param f function to apply to each element. + * @return [f(a0), ..., f(an)] if this list is [a0, ..., an]. + */ + final override def map[B](f: A => B): List[B] = { + val b = new ListBuffer[B] + var these = this + while (!these.isEmpty) { + b += f(these.head) + these = these.tail + } + b.toList + } + + /** Apply a function to all the elements of the list, and return the + * reversed list of results. This is equivalent to a call to map + * followed by a call to reverse, but more efficient. + * + * @param f the function to apply to each elements. + * @return the reversed list of results. + */ + def reverseMap[B](f: A => B): List[B] = { + def loop(l: List[A], res: List[B]): List[B] = l match { + case Nil => res + case head :: tail => loop(tail, f(head) :: res) + } + loop(this, Nil) + } + + /** Apply the given function f to each element of this list + * (while respecting the order of the elements). + * + * @param f the treatment to apply to each element. + */ + final override def foreach(f: A => Unit) { + var these = this + while (!these.isEmpty) { + f(these.head) + these = these.tail + } + } + + /** Returns all the elements of this list that satisfy the + * predicate p. The order of the elements is preserved. + * It is guarenteed that the receiver list itself is returned iff all its + * elements satisfy the predicate `p'. Hence the following equality is valid: + * + * (xs filter p) eq xs == xs forall p + * + * @param p the predicate used to filter the list. + * @return the elements of this list satisfying p. + */ + final override def filter(p: A => Boolean): List[A] = { + // return same list if all elements satisfy p + var these = this + while (!these.isEmpty && p(these.head)) { + these = these.tail + } + if (these.isEmpty) this + else { + val b = new ListBuffer[A] + var these1 = this + while (these1 ne these) { + b += these1.head + these1 = these1.tail + } + + these = these.tail // prevent the second evaluation of the predicate + // on the element on which it first failed + while (!these.isEmpty) { + if (p(these.head)) b += these.head + these = these.tail + } + b.toList + } + } + + /**

+ * Sort the list according to the comparison function + * <(e1: a, e2: a) => Boolean, + * which should be true iff e1 is smaller than + * e2. + *

+ * + * @param lt the comparison function + * @return a list sorted according to the comparison function + * <(e1: a, e2: a) => Boolean. + * @ex
+   *    List("Steve", "Tom", "John", "Bob")
+   *      .sort((e1, e2) => (e1 compareTo e2) < 0) =
+   *    List("Bob", "John", "Steve", "Tom")
+ */ + def sort(lt : (A,A) => Boolean): List[A] = { + /** Merge two already-sorted lists */ + def merge(l1: List[A], l2: List[A]): List[A] = { + val res = new ListBuffer[A] + var left1 = l1 + var left2 = l2 + + while (!left1.isEmpty && !left2.isEmpty) { + if(lt(left1.head, left2.head)) { + res += left1.head + left1 = left1.tail + } else { + res += left2.head + left2 = left2.tail + } + } + + res ++= left1 + res ++= left2 + + res.toList + } + + /** Split a list into two lists of about the same size */ + def split(lst: List[A]) = { + val res1 = new ListBuffer[A] + val res2 = new ListBuffer[A] + var left = lst + + while (!left.isEmpty) { + res1 += left.head + left = left.tail + if (!left.isEmpty) { + res2 += left.head + left = left.tail + } + } + + (res1.toList, res2.toList) + } + + + /** Merge-sort the specified list */ + def ms(lst: List[A]): List[A] = + lst match { + case Nil => lst + case x :: Nil => lst + case x :: y :: Nil => + if (lt(x,y)) + lst + else + y :: x :: Nil + + case lst => + val (l1, l2) = split(lst) + val l1s = ms(l1) + val l2s = ms(l2) + merge(l1s, l2s) + } + + ms(this) + } + + /** Tests if the predicate p is satisfied by all elements + * in this list. + * + * @param p the test predicate. + * @return true iff all elements of this list satisfy the + * predicate p. + */ + override def forall(p: A => Boolean): Boolean = { + var these = this + while (!these.isEmpty) { + if (!p(these.head)) return false + these = these.tail + } + true + } + + /** Tests the existence in this list of an element that satisfies the + * predicate p. + * + * @param p the test predicate. + * @return true iff there exists an element in this list that + * satisfies the predicate p. + */ + override def exists(p: A => Boolean): Boolean = { + var these = this + while (!these.isEmpty) { + if (p(these.head)) return true + these = these.tail + } + false + } + + /** Find and return the first element of the list satisfying a + * predicate, if any. + * + * @param p the predicate + * @return the first element in the list satisfying p, + * or None if none exists. + */ + override def find(p: A => Boolean): Option[A] = { + var these = this + while (!these.isEmpty) { + if (p(these.head)) return Some(these.head) + these = these.tail + } + None + } + + /** Combines the elements of this list together using the binary + * function f, from left to right, and starting with + * the value z. + * + * @return f(... (f(f(z, a0), a1) ...), + * an) if the list is + * [a0, a1, ..., an]. + */ + override def foldLeft[B](z: B)(f: (B, A) => B): B = { + var acc = z + var these = this + while (!these.isEmpty) { + acc = f(acc, these.head) + these = these.tail + } + acc + } + + /** Combines the elements of this list together using the binary + * function f, from right to left, and starting with + * the value z. + * + * @return f(a0, f(a1, f(..., f(an, z)...))) + * if the list is [a0, a1, ..., an]. + */ + override def foldRight[B](z: B)(f: (A, B) => B): B = this match { + case Nil => z + case x :: xs => f(x, xs.foldRight(z)(f)) + } + + /** Combines the elements of this list together using the binary + * operator op, from left to right + * @param op The operator to apply + * @return op(... op(a0,a1), ..., an) + if the list has elements + * a0, a1, ..., an. + * @throws Predef.UnsupportedOperationException if the list is empty. + */ + override def reduceLeft[B >: A](f: (B, A) => B): B = this match { + case Nil => throw new UnsupportedOperationException("Nil.reduceLeft") + case x :: Nil => x + case x0 :: x1 :: xs => + var acc : B = f(x0, x1) + var these : List[A] = xs + while (!these.isEmpty) { + acc = f(acc, these.head) + these = these.tail + } + acc + } + + /** Combines the elements of this list together using the binary + * operator op, from right to left + * @param op The operator to apply + * + * @return a0 op (... op (an-1 op an)...) + * if the list has elements a0, a1, ..., + * an. + * + * @throws Predef.UnsupportedOperationException if the list is empty. + */ + override def reduceRight[B >: A](f: (A, B) => B): B = this match { + case Nil => throw new UnsupportedOperationException("Nil.reduceRight") + case x :: Nil => x + case x :: xs => f(x, xs reduceRight f) + } + + /** Applies the given function f to each element of + * this list, then concatenates the results. + * + * @param f the function to apply on each element. + * @return f(a0) ::: ... ::: f(an) if + * this list is [a0, ..., an]. + */ + final override def flatMap[B](f: A => Iterable[B]): List[B] = { + val b = new ListBuffer[B] + var these = this + while (!these.isEmpty) { + var those = f(these.head).elements + while (those.hasNext) { + b += those.next + } + these = these.tail + } + b.toList + } + + /** A list consisting of all elements of this list in reverse order. + */ + override def reverse: List[A] = { + var result: List[A] = Nil + var these = this + while (!these.isEmpty) { + result = these.head :: result + these = these.tail + } + result + } + + /** Returns a list formed from this list and the specified list + * that by associating each element of the former with + * the element at the same position in the latter. + * If one of the two lists is longer than the other, its remaining elements are ignored. + * + * @return List((a0,b0), ..., + * (amin(m,n),bmin(m,n))) when + * List(a0, ..., am) + * zip List(b0, ..., bn) is invoked. + */ + def zip[B](that: List[B]): List[(A, B)] = { + val b = new ListBuffer[(A, B)] + var these = this + var those = that + while (!these.isEmpty && !those.isEmpty) { + b += ((these.head, those.head)) + these = these.tail + those = those.tail + } + b.toList + } + + /** Returns a list formed from this list and the specified list + * that by associating each element of the former with + * the element at the same position in the latter. + * + * @param that list that may have a different length + * as the self list. + * @param thisElem element thisElem is used to fill up the + * resulting list if the self list is shorter than + * that + * @param thatElem element thatElem is used to fill up the + * resulting list if that is shorter than + * the self list + * @return List((a0,b0), ..., + * (an,bn), (elem,bn+1), + * ..., {elem,bm}) + * when [a0, ..., an] zip + * [b0, ..., bm] is + * invoked where m > n. + */ + def zipAll[B, C >: A, D >: B](that: List[B], thisElem: C, thatElem: D): List[(C, D)] = { + val b = new ListBuffer[(C, D)] + var these = this + var those = that + while (!these.isEmpty && !those.isEmpty) { + b += ((these.head, those.head)) + these = these.tail + those = those.tail + } + while (!these.isEmpty) { + b += ((these.head, thatElem)) + these = these.tail + } + while (!those.isEmpty) { + b += ((thisElem, those.head)) + those = those.tail + } + b.toList + } + + /** Computes the union of this list and the given list + * that. + * + * @param that the list of elements to add to the list. + * @return a list without doubles containing the elements of this + * list and those of the given list that. + */ + def union[B >: A](that: List[B]): List[B] = { + val b = new ListBuffer[B] + var these = this + while (!these.isEmpty) { + if (!that.contains(these.head)) b += these.head + these = these.tail + } + b.prependToList(that) + } + + /** Computes the difference between this list and the given list + * that. + * + * @param that the list of elements to remove from this list. + * @return this list without the elements of the given list + * that. + * @deprecated use -- instead + */ + @deprecated + def diff[B >: A](that: List[B]): List[B] = this -- that + + /** Computes the difference between this list and the given list + * that. + * + * @param that the list of elements to remove from this list. + * @return this list without the elements of the given list + * that. + */ + def -- [B >: A](that: List[B]): List[B] = { + val b = new ListBuffer[B] + var these = this + while (!these.isEmpty) { + if (!that.contains(these.head)) b += these.head + these = these.tail + } + b.toList + } + + /** Computes the difference between this list and the given object + * x. + * + * @param x the object to remove from this list. + * @return this list without the elements of the given object + * x. + */ + def - [B >: A](x: B): List[B] = { + val b = new ListBuffer[B] + var these = this + while (!these.isEmpty) { + if (these.head != x) b += these.head + these = these.tail + } + b.toList + } + + /** Concatenate the elements of this list. The elements of this list + * should be a Iterables. + * + * Note: The compiler might not be able to infer the type parameter. + * + * @param f An implicit conversion to an Iterable instance. + * @return The concatenation of all elements of iterables in this list. + */ + def flatten[B](implicit f : A => Iterable[B]) : List[B] = { + val buf = new ListBuffer[B] + foreach(f(_).foreach(buf += _)) + buf.toList + } + + override def stringPrefix = "List" + + override def toStream : Stream[A] = null // !!! + /*new Stream.Definite[A] { + override def force : List[A] = List.this + override def isEmpty = List.this.isEmpty + override def head = List.this.head + override def tail = List.this.tail.toStream + protected def addDefinedElems(buf: StringBuilder, prefix: String): StringBuilder = if (!isEmpty) { + var prefix0 = prefix + var buf1 = buf.append(prefix0).append(head) + prefix0 = ", " + var tail0 = tail + while (!tail0.isEmpty) { + buf1 = buf.append(prefix0).append(tail0.head) + tail0 = tail0.tail + } + buf1 + } else buf + }*/ + +} + +/** The empty list. + * + * @author Martin Odersky + * @version 1.0, 15/07/2003 + */ +@SerialVersionUID(0 - 8256821097970055419L) +case object Nil extends List[Nothing] { + override def isEmpty = true + override def head: Nothing = + throw new NoSuchElementException("head of empty list") + override def tail: List[Nothing] = + throw new NoSuchElementException("tail of empty list") +} + +/** A non empty list characterized by a head and a tail. + * + * @author Martin Odersky + * @version 1.0, 15/07/2003 + */ +@SerialVersionUID(0L - 8476791151983527571L) +final case class ::[B](private var hd: B, private[scalax] var tl: List[B]) extends List[B] { + override def head : B = hd + override def tail : List[B] = tl + override def isEmpty: Boolean = false + + import java.io._ + + private def writeObject(out: ObjectOutputStream) { + var xs: List[B] = this + while (!xs.isEmpty) { out.writeObject(xs.head); xs = xs.tail } + out.writeObject(ListSerializeEnd) + } + + private def readObject(in: ObjectInputStream) { + hd = in.readObject.asInstanceOf[B] + assert(hd != ListSerializeEnd) + var current: ::[B] = this + while (true) in.readObject match { + case ListSerializeEnd => + current.tl = Nil + return + case a : Any => + val list : ::[B] = new ::(a.asInstanceOf[B], Nil) + current.tl = list + current = list + } + } +} + +/** Only used for list serialization */ +@SerialVersionUID(0L - 8476791151975527571L) +private[scalax] case object ListSerializeEnd +/** This object provides methods for creating specialized lists, and for + * transforming special kinds of lists (e.g. lists of lists). + * + * @author Martin Odersky and others + * @version 1.0, 15/07/2003 + */ +object List { + + /** Create a list with given elements. + * + * @param xs the elements to put in the list + * @return the list containing elements xs. + */ + def apply[A](xs: A*): List[A] = (xs.asInstanceOf[Iterable[A]]).toList // !!! + + /** for unapply matching + */ + def unapplySeq[A](x: List[A]): Some[List[A]] = Some(x) + + /** Create a sorted list of all integers in a range. + * + * @param from the start value of the list + * @param end the end value of the list + * @return the sorted list of all integers in range [from;end). + */ + def range(start: Int, end: Int): List[Int] = + range(start, end, 1) + + /** Create a list with element values + * vn+1 = vn + step + * where v0 = start + * and elements are in the range between start (inclusive) + * and end (exclusive) + * + * @param start the start value of the list + * @param end the end value of the list + * @param step the increment value of the list + * @return the sorted list of all integers in range [start;end). + */ + def range(start: Int, end: Int, step: Int): List[Int] = { + val b = new ListBuffer[Int] + var i = start + while ((step <= 0 || i < end) && (step >= 0 || i > end)) { + b += i + i += step + } + b.toList + } + + /** Create a sorted list with element values + * vn+1 = step(vn) + * where v0 = start + * and elements are in the range between start (inclusive) + * and end (exclusive) + * + * @param start the start value of the list + * @param end the end value of the list + * @param step the increment function of the list, must be monotonically increasing or decreasing + * @return the sorted list of all integers in range [start;end). + */ + def range(start: Int, end: Int, step: Int => Int): List[Int] = { + val up = step(start) > start + val down = step(start) < start + val b = new ListBuffer[Int] + var i = start + while ((!up || i < end) && (!down || i > end)) { + b += i + i += step(i) + } + b.toList + } + + /** Create a list containing several copies of an element. + * + * @param n the length of the resulting list + * @param elem the element composing the resulting list + * @return a list composed of n elements all equal to elem + */ + def make[A](n: Int, elem: A): List[A] = { + val b = new ListBuffer[A] + var i = 0 + while (i < n) { + b += elem + i += 1 + } + b.toList + } + + /** Create a list by applying a function to successive integers. + * + * @param n the length of the resulting list + * @param maker the procedure which, given an integer n, + * returns the nth element of the resulting list, where + * n is in interval [0;n). + * @return the list obtained by applying the maker function to + * successive integers from 0 to n (exclusive). + */ + def tabulate[A](n: Int, maker: Int => A): List[A] = { + val b = new ListBuffer[A] + var i = 0 + while (i < n) { + b += maker(i) + i += 1 + } + b.toList + } + + /** Concatenate all the elements of a given list of lists. + * + * @param xss the list of lists that are to be concatenated + * @return the concatenation of all the lists + */ + def flatten[A](xss: List[List[A]]): List[A] = { + val b = new ListBuffer[A] + for (xs <- xss) { + var xc = xs + while (!xc.isEmpty) { + b += xc.head + xc = xc.tail + } + } + b.toList + } + + /** Concatenate all the argument lists into a single list. + * + * @param xss the lists that are to be concatenated + * @return the concatenation of all the lists + */ + def concat[A](xss: List[A]*): List[A] = { + val b = new ListBuffer[A] + for (xs <- xss) { + var xc = xs + while (!xc.isEmpty) { + b += xc.head + xc = xc.tail + } + } + b.toList + } + + /** Transforms a list of pairs into a pair of lists. + * + * @param xs the list of pairs to unzip + * @return a pair of lists. + */ + def unzip[A,B](xs: List[(A,B)]): (List[A], List[B]) = { + val b1 = new ListBuffer[A] + val b2 = new ListBuffer[B] + var xc = xs + while (!xc.isEmpty) { + b1 += xc.head._1 + b2 += xc.head._2 + xc = xc.tail + } + (b1.toList, b2.toList) + } + + /** Transforms an iterable of pairs into a pair of lists. + * + * @param xs the iterable of pairs to unzip + * @return a pair of lists. + */ + def unzip[A,B](xs: Iterable[(A,B)]): (List[A], List[B]) = + xs.foldRight[(List[A], List[B])]((Nil, Nil)) { + case ((x, y), (xs, ys)) => (x :: xs, y :: ys) + } + + /** + * Returns the Left values in the given Iterable of Eithers. + */ + def lefts[A, B](es: Iterable[Either[A, B]]) = + es.foldRight[List[A]](Nil)((e, as) => e match { + case Left(a) => a :: as + case Right(_) => as + }) + + /** + * Returns the Right values in the givenIterable of Eithers. + */ + def rights[A, B](es: Iterable[Either[A, B]]) = + es.foldRight[List[B]](Nil)((e, bs) => e match { + case Left(_) => bs + case Right(b) => b :: bs + }) + + /** Transforms an Iterable of Eithers into a pair of lists. + * + * @param xs the iterable of Eithers to separate + * @return a pair of lists. + */ + def separate[A,B](es: Iterable[Either[A,B]]): (List[A], List[B]) = + es.foldRight[(List[A], List[B])]((Nil, Nil)) { + case (Left(a), (lefts, rights)) => (a :: lefts, rights) + case (Right(b), (lefts, rights)) => (lefts, b :: rights) + } + + /** Converts an iterator to a list. + * + * @param it the iterator to convert + * @return a list that contains the elements returned by successive + * calls to it.next + */ + def fromIterator[A](it: Iterator[A]): List[A] = it.toList + + /** Converts an array into a list. + * + * @param arr the array to convert + * @return a list that contains the same elements than arr + * in the same order + */ + def fromArray[A](arr: Array[A]): List[A] = fromArray(arr, 0, arr.length) + + /** Converts a range of an array into a list. + * + * @param arr the array to convert + * @param start the first index to consider + * @param len the lenght of the range to convert + * @return a list that contains the same elements than arr + * in the same order + */ + def fromArray[A](arr: Array[A], start: Int, len: Int): List[A] = { + var res: List[A] = Nil + var i = start + len + while (i > start) { + i -= 1 + res = arr(i) :: res + } + res + } + + /** Parses a string which contains substrings separated by a + * separator character and returns a list of all substrings. + * + * @param str the string to parse + * @param separator the separator character + * @return the list of substrings + */ + def fromString(str: String, separator: Char): List[String] = { + var words: List[String] = Nil + var pos = str.length() + while (pos > 0) { + val pos1 = str.lastIndexOf(separator, pos - 1) + if (pos1 + 1 < pos) + words = str.substring(pos1 + 1, pos) :: words + pos = pos1 + } + words + } + + /** Returns the given string as a list of characters. + * + * @param str the string to convert. + * @return the string as a list of characters. + * @deprecated use str.toList instead + */ + @deprecated def fromString(str: String): List[Char] = + str.toList.asInstanceOf[List[Char]] // !!! + + /** Returns the given list of characters as a string. + * + * @param xs the list to convert. + * @return the list in form of a string. + */ + def toString(xs: List[Char]): String = { + val sb = new StringBuilder() + var xc = xs + while (!xc.isEmpty) { + sb.append(xc.head) + xc = xc.tail + } + sb.toString() + } + + /** Like xs map f, but returns xs unchanged if function + * f maps all elements to themselves. + * + * @param xs ... + * @param f ... + * @return ... + */ + def mapConserve[A <: AnyRef](xs: List[A])(f: A => A): List[A] = { + def loop(ys: List[A]): List[A] = + if (ys.isEmpty) xs + else { + val head0 = ys.head + val head1 = f(head0) + if (head1 eq head0) { + loop(ys.tail) + } else { + val ys1 = head1 :: mapConserve(ys.tail)(f) + if (xs eq ys) ys1 + else { + val b = new ListBuffer[A] + var xc = xs + while (xc ne ys) { + b += xc.head + xc = xc.tail + } + b.prependToList(ys1) + } + } + } + loop(xs) + } + + /** Returns the list resulting from applying the given function f + * to corresponding elements of the argument lists. + * + * @param f function to apply to each pair of elements. + * @return [f(a0,b0), ..., f(an,bn)] if the lists are + * [a0, ..., ak], [b0, ..., bl] and + * n = min(k,l) + */ + def map2[A,B,C](xs: List[A], ys: List[B])(f: (A, B) => C): List[C] = { + val b = new ListBuffer[C] + var xc = xs + var yc = ys + while (!xc.isEmpty && !yc.isEmpty) { + b += f(xc.head, yc.head) + xc = xc.tail + yc = yc.tail + } + b.toList + } + + /** Returns the list resulting from applying the given function + * f to corresponding elements of the argument lists. + * + * @param f function to apply to each pair of elements. + * @return [f(a0,b0,c0), + * ..., f(an,bn,cn)] + * if the lists are [a0, ..., ak], + * [b0, ..., bl], + * [c0, ..., cm] and + * n = min(k,l,m) + */ + def map3[A,B,C,D](xs: List[A], ys: List[B], zs: List[C])(f: (A, B, C) => D): List[D] = { + val b = new ListBuffer[D] + var xc = xs + var yc = ys + var zc = zs + while (!xc.isEmpty && !yc.isEmpty && !zc.isEmpty) { + b += f(xc.head, yc.head, zc.head) + xc = xc.tail + yc = yc.tail + zc = zc.tail + } + b.toList + } + + /** Tests whether the given predicate p holds + * for all corresponding elements of the argument lists. + * + * @param p function to apply to each pair of elements. + * @return (p(a0,b0) && + * ... && p(an,bn))] + * if the lists are [a0, ..., ak]; + * [b0, ..., bl] + * and n = min(k,l) + */ + def forall2[A,B](xs: List[A], ys: List[B])(f: (A, B) => Boolean): Boolean = { + var xc = xs + var yc = ys + while (!xc.isEmpty && !yc.isEmpty) { + if (!f(xc.head, yc.head)) return false + xc = xc.tail + yc = yc.tail + } + true + } + + /** Tests whether the given predicate p holds + * for some corresponding elements of the argument lists. + * + * @param p function to apply to each pair of elements. + * @return n != 0 && (p(a0,b0) || + * ... || p(an,bn))] if the lists are + * [a0, ..., ak], + * [b0, ..., bl] and + * n = min(k,l) + */ + def exists2[A,B](xs: List[A], ys: List[B])(f: (A, B) => Boolean): Boolean = { + var xc = xs + var yc = ys + while (!xc.isEmpty && !yc.isEmpty) { + if (f(xc.head, yc.head)) return true + xc = xc.tail + yc = yc.tail + } + false + } + + /** Transposes a list of lists. + * pre: All element lists have the same length. + * + * @param xss the list of lists + * @return the transposed list of lists + */ + def transpose[A](xss: List[List[A]]): List[List[A]] = { + val buf = new ListBuffer[List[A]] + var yss = xss + while (!yss.head.isEmpty) { + buf += (yss map (_.head)) + yss = (yss map (_.tail)) + } + buf.toList + } + + /** Lists with ordered elements are ordered + implicit def list2ordered[a <% Ordered[a]](x: List[a]): Ordered[List[a]] = new Ordered[List[a]] { + def compare [b >: List[a] <% Ordered[b]](y: b): Int = y match { + case y1: List[a] => compareLists(x, y1); + case _ => -(y compare x) + } + private def compareLists(xs: List[a], ys: List[a]): Int = { + if (xs.isEmpty && ys.isEmpty) 0 + else if (xs.isEmpty) -1 + else if (ys.isEmpty) 1 + else { + val s = xs.head compare ys.head; + if (s != 0) s + else compareLists(xs.tail, ys.tail) + } + } + } + */ +} + diff --git a/src/library/scalax/collection/mutable/Appendable.scala b/src/library/scalax/collection/mutable/Appendable.scala new file mode 100644 index 0000000000..b8e8569a14 --- /dev/null +++ b/src/library/scalax/collection/mutable/Appendable.scala @@ -0,0 +1,94 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Iterable.scala 15188 2008-05-24 15:01:02Z stepancheg $ + +package scalax.collection.mutable + +/** This class represents collections that can be augmented using a += operator. + * + * @autor Martin Odersky + * @owner Martin Odersky + * @version 2.8 + */ +trait Appendable[A] { + + /** Append a single element to this buffer. + * + * @param elem the element to append. + */ + def +=(elem: A): Unit + + /** Append a two or more elements to this buffer. + * + * @param elem1 the first element to append. + * @param elem2 the second element to append. + * @param elems the remaining elements to append. + */ + def +=(elem1: A, elem2: A, elems: A*) { + this += elem1 + this += elem2 + this ++= elems.asInstanceOf[Iterable[A]] // !!! + } + + /** Appends a number of elements provided by an iterator + * + * @param iter the iterator. + */ + def ++=(iter: collection.Iterator[A]) { iter foreach += } + + /** Appends a number of elements provided by an iterable object + * via its elements method. + * + * @param iter the iterable object. + */ + def ++=(iter: collection.Iterable[A]) { iter foreach += } + + /** Append a single element to this buffer and return + * the identity of the buffer. + * + * @param elem the element to append. + */ + def +(elem: A): this.type = { this += elem; this } + + /** Append two or more elements to this buffer and return + * the identity of the buffer. + * + * @param elem1 the first element to append. + * @param elem2 the second element to append. + * @param elems the remaining elements to append. + */ + def +(elem1: A, elem2: A, elems: A*): this.type = + this + elem1 + elem2 ++ elems.asInstanceOf[Iterable[A]] // !!! + + /** Appends a number of elements provided by an iterable object + * via its elements method. The identity of the + * buffer is returned. + * + * @param iter the iterable object. + * @return the updated buffer. + */ + def ++(iter: Iterable[A]): this.type = { this ++= iter; this } + + /** Appends a number of elements provided by an iterator + * via its elements method. The identity of the + * buffer is returned. + * + * @param iter the iterator + * @return the updated buffer. + */ + def ++(iter: Iterator[A]): this.type = { this ++= iter; this } + + /** Clears the buffer contents. + */ + def clear() +} + + + + diff --git a/src/library/scalax/collection/mutable/ArrayBuffer.scala b/src/library/scalax/collection/mutable/ArrayBuffer.scala new file mode 100644 index 0000000000..3e4877cef7 --- /dev/null +++ b/src/library/scalax/collection/mutable/ArrayBuffer.scala @@ -0,0 +1,118 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2007, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: ArrayBuffer.scala 15407 2008-06-20 09:26:36Z stepancheg $ + + +package scalax.collection.mutable + +/** An implementation of the Buffer class using an array to + * represent the assembled sequence internally. Append, update and random + * access take constant time (amortized time). Prepends and removes are + * linear in the buffer size. + * + * @author Matthias Zenger + * @author Martin Odersky + * @version 2.8 + */ +@serializable +class ArrayBuffer[A] extends Buffer[A] with Builder[ArrayBuffer, A] with ResizableArray[A] { + + def clear() { + size0 = 0 + } + + /** Appends a single element to this buffer and returns + * the identity of the buffer. It takes constant time. + * + * @param elem the element to append. + */ + def +=(elem: A) { + ensureSize(size0 + 1) + array(size0) = elem.asInstanceOf[AnyRef] + size0 += 1 + } + + /** Appends a number of elements provided by an iterable object + * via its elements method. The identity of the + * buffer is returned. + * + * @param iter the iterable object. + * @return the updated buffer. + */ + override def ++=(iter: Iterable[A]) { iter copyToBuffer this } + + /** Prepends a single element to this buffer and return + * the identity of the buffer. It takes time linear in + * the buffer size. + * + * @param elem the element to append. + * @return the updated buffer. + */ + def +:(elem: A): this.type = { + ensureSize(size0 + 1) + copy(0, 1, size0) + array(0) = elem.asInstanceOf[AnyRef] + size0 += 1 + this + } + + /** Prepends a number of elements provided by an iterable object + * via its elements method. The identity of the + * buffer is returned. + * + * @param iter the iterable object. + * @return the updated buffer. + */ + override def ++:(iter: Iterable[A]): this.type = { insertAll(0, iter); this } + + /** Inserts new elements at the index n. Opposed to method + * update, this method will not replace an element with a + * one. Instead, it will insert a new element at index n. + * + * @param n the index where a new element will be inserted. + * @param iter the iterable object providing all elements to insert. + * @throws Predef.IndexOutOfBoundsException if n is out of bounds. + */ + def insertAll(n: Int, iter: Iterable[A]) { + if ((n < 0) || (n > size0)) + throw new IndexOutOfBoundsException(n.toString) + val xs = iter.elements.toList + val len = xs.length + ensureSize(size0 + len) + copy(n, n + len, size0 - n) + xs.copyToArray(array.asInstanceOf[Array[Any]], n) + size0 += len + } + + /** Removes the element on a given index position. It takes time linear in + * the buffer size. + * + * @param n the index which refers to the element to delete. + * @return the updated array buffer. + * @throws Predef.IndexOutOfBoundsException if n is out of bounds. + */ + def remove(n: Int, count: Int) { + if ((n < 0) || (n >= size0)) + throw new IndexOutOfBoundsException(n.toString) + copy(n + count, n, size0 - (n + count)) + size0 -= count + } + + /** Return a clone of this buffer. + * + * @return an ArrayBuffer with the same elements. + */ + override def clone(): Buffer[A] = new ArrayBuffer[A] ++ this + + def result: ArrayBuffer[A] = this + + /** Defines the prefix of the string representation. + */ + override def stringPrefix: String = "ArrayBuffer" +} diff --git a/src/library/scalax/collection/mutable/Buffer.scala b/src/library/scalax/collection/mutable/Buffer.scala new file mode 100644 index 0000000000..c11fa37409 --- /dev/null +++ b/src/library/scalax/collection/mutable/Buffer.scala @@ -0,0 +1,264 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2007, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Buffer.scala 15799 2008-08-15 18:23:54Z odersky $ + + +package scalax.collection.mutable + +import generic.mutable.VectorTemplate + +/** Buffers are used to create sequences of elements incrementally by + * appending, prepending, or inserting new elements. It is also + * possible to access and modify elements in a random access fashion + * via the index of the element in the current sequence. + * + * @author Matthias Zenger + * @author Martin Odersky + * @version 2.8 + */ +@cloneable +trait Buffer[A] extends Vector[A] with VectorTemplate[Buffer, A] with Appendable[A] +// with Scriptable[Message[(Location, A)]] + with CloneableCollection +{ + +// Abstract methods from Vector: + + /** Return element at index `n` + * @throws IndexOutofBoundsException if the index is not valid + */ + def apply(n: Int): A + + /** Return number of elements in the buffer + */ + def length: Int + + /** Create a new buffer of the same kind as this one */ + def newBuilder[B]: Builder[Buffer, B] = new ArrayBuffer[B] + + /** Replace element at index n with the new element + * newelem. + * + * @param n the index of the element to replace. + * @param newelem the new element. + * @throws IndexOutofBoundsException if the index is not valid + */ + def update(n: Int, newelem: A): Unit + +// Abstract methods from Appendable + + /** Append a single element to this buffer. + * + * @param elem the element to append. + */ + def +=(elem: A): Unit + + /** Clears the buffer contents. + */ + def clear() + +// Abstract methods new in this class + + /** Prepend a single element to this buffer and return + * the identity of the buffer. + * @param elem the element to prepend. + */ + def +:(elem: A): this.type + + /** Inserts new elements at the index n. Opposed to method + * update, this method will not replace an element with a + * one. Instead, it will insert a new element at index n. + * + * @param n the index where a new element will be inserted. + * @param iter the iterable object providing all elements to insert. + * @throws IndexOutofBoundsException if the index is not valid + */ + def insertAll(n: Int, iter: Iterable[A]): Unit + + /** Removes a number of elements from a given index position. + * + * @param n the index which refers to the element to delete. + * @param count the number of elements to delete + * @throws IndexOutofBoundsException if the index is not valid + */ + def remove(n: Int, count: Int): Unit + +// Concrete methods + + /** Removes the element on a given index position. + * + * @param n the index which refers to the element to delete. + */ + def remove(n: Int): A = { + val elem = this(n) + remove(n, 1) + elem + } + + /** Removes a single element from this buffer, at its first occurrence. + * If the list does not contain that element, it is unchanged + * + * @param x the element to remove. + */ + def -= (x: A) { + val i = indexOf(x) + if (i != -1) remove(i) + } + + /** Removes a single element from this buffer, at its first occurrence, + * and returns the identity of the buffer. + * If the buffer does not contain that element, it is unchanged + * + * @param x the element to remove. + */ + def - (x: A): this.type = { -=(x); this } + + /** Prepend two ore more elements to this buffer and return + * the identity of the buffer. + * @param elem the element to prepend. + */ + def +:(elem1: A, elem2: A, elems: A*): this.type = + elem1 +: elem2 +: (elems.asInstanceOf[Iterable[A]]) ++: this // !!! + + /** Prepends a number of elements provided by an iterable object + * via its elements method. The identity of the + * buffer is returned. + * + * @param iter the iterable object. + */ + def ++:(iter: Iterable[A]): this.type = { for (x <- iter) x +: this; this } + + /** Prepends a number of elements provided by an iterator + * The identity of the buffer is returned. + * + * @param iter the iterator + * @return the updated buffer. + */ + def ++:(iter: Iterator[A]): this.type = { for (x <- iter) x +: this; this } + + /** Appends a elements to this buffer. + * + * @param elems the elements to append. + */ + def append(elems: A*) { this ++= elems.asInstanceOf[Iterable[A]] } // !!! + + /** Appends a number of elements provided by an iterable object + * via its elements method. + * + * @param iter the iterable object. + */ + def appendAll(iter: Iterable[A]) { this ++= iter } + + /** Prepend given elements to this list. + * + * @param elem the element to prepend. + */ + def prepend(elems: A*) { elems.asInstanceOf[Iterable[A]] ++: this } // !!! + + /** Prepends a number of elements provided by an iterable object + * via its elements method. The identity of the + * buffer is returned. + * + * @param iter the iterable object. + */ + def prependAll(iter: Iterable[A]) { iter ++: this } + + /** Prepends a number of elements provided by an iterable object + * via its elements method. The identity of the + * buffer is returned. + * + * @param iter the iterable object. + */ + def prependAll(iter: Iterator[A]) { iter ++: this } + + /** Inserts new elements at the index n. Opposed to method + * update, this method will not replace an element with a + * one. Instead, it will insert the new elements at index n. + * + * @param n the index where a new element will be inserted. + * @param elems the new elements to insert. + */ + def insert(n: Int, elems: A*) { insertAll(n, elems.asInstanceOf[Iterable[A]]) } // !!! + + /** Removes the first n elements. + * + * @param n the number of elements to remove from the beginning + * of this buffer. + */ + def trimStart(n: Int) { remove(0, n) } + + /** Removes the last n elements. + * + * @param n the number of elements to remove from the end + * of this buffer. + */ + def trimEnd(n: Int) { remove(length - n max 0, n) } + + /** Send a message to this scriptable object. + * + * @param cmd the message to send. + * !!!! todo: rewrite location, msg etc with enumerations or else pack in a subpackage + def <<(cmd: Message[(Location, A)]) { + cmd match { + case Include((l, elem)) => l match { + case Start => prepend(elem) + case End => append(elem) + case Index(n) => insert(n, elem) + case _ => throw new UnsupportedOperationException("message " + cmd + " not understood") + } + case Update((l, elem)) => l match { + case Start => update(0, elem) + case End => update(length - 1, elem) + case Index(n) => update(n, elem) + case _ => throw new UnsupportedOperationException("message " + cmd + " not understood") + } + case Remove((l, _)) => l match { + case Start => remove(0) + case End => remove(length - 1) + case Index(n) => remove(n) + case _ => throw new UnsupportedOperationException("message " + cmd + " not understood") + } + case Reset() => clear + case s: Script[_] => s.elements foreach << + case _ => throw new UnsupportedOperationException("message " + cmd + " not understood") + } + } + */ + + /** Return a clone of this buffer. + * + * @return a buffer with the same elements. + */ + override def clone(): Buffer[A] = super.clone().asInstanceOf[Buffer[A]] + + /** Defines the prefix of the string representation. + */ + override def stringPrefix: String = "Buffer" + + /** Adds a number of elements in an array + * + * @param src the array + * @param start the first element to append + * @param len the number of elements to append + * @deprecated replace by: buf ++= src.view(start, end) + */ + @deprecated def ++=(src: Array[A], start: Int, len: Int) { + var i = start + val end = i + len + while (i < end) { + this += src(i) + i += 1 + } + } + +} + + + + diff --git a/src/library/scalax/collection/mutable/CloneableCollection.scala b/src/library/scalax/collection/mutable/CloneableCollection.scala new file mode 100644 index 0000000000..ab2c210134 --- /dev/null +++ b/src/library/scalax/collection/mutable/CloneableCollection.scala @@ -0,0 +1,19 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2007, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: CloneableCollection.scala 12003 2007-06-13 12:14:15Z mihaylov $ + + +package scalax.collection.mutable + +/** The J2ME version of the library defined this trait with a clone method + * to substitute for the lack of Object.clone there + */ +trait CloneableCollection { + override def clone(): AnyRef = super.clone() +} diff --git a/src/library/scalax/collection/mutable/ListBuffer.scala b/src/library/scalax/collection/mutable/ListBuffer.scala new file mode 100644 index 0000000000..adc8cbdb26 --- /dev/null +++ b/src/library/scalax/collection/mutable/ListBuffer.scala @@ -0,0 +1,288 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2007, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: ListBuffer.scala 14378 2008-03-13 11:39:05Z dragos $ + + +package scalax.collection.mutable + +import generic.SequenceForwarder +import immutable.List +import collection.immutable.{List, Nil, ::} + +/** A Buffer implementation back up by a list. It provides constant time + * prepend and append. Most other operations are linear. + * + * @author Matthias Zenger + * @author Martin Odersky + * @version 2.8 + */ +@serializable +final class ListBuffer[A] + extends Buffer[A] + with Builder[List, A] + with SequenceForwarder[A] +{ + private var start: List[A] = Nil + private var last0: ::[A] = _ + private var exported: Boolean = false + + protected def underlying: Sequence[A] = start + + // Implementations of abstract methods in Buffer + + /** Replaces element at index n with the new element + * newelem. Takes time linear in the buffer size. (except the first + * element, which is updated in constant time). + * + * @param n the index of the element to replace. + * @param x the new element. + * @throws Predef.IndexOutOfBoundsException if n is out of bounds. + */ + def update(n: Int, x: A) { + try { + if (exported) copy() + if (n == 0) { + val newElem = new :: (x, start.tail); + if (last0 eq start) last0 = newElem + start = newElem + } else { + var cursor = start + var i = 1 + while (i < n) { + cursor = cursor.tail + i += 1 + } + val newElem = new :: (x, cursor.tail.tail) + if (last0 eq cursor.tail) last0 = newElem + cursor.asInstanceOf[::[A]].tl = newElem + } + } catch { + case ex: Exception => throw new IndexOutOfBoundsException(n.toString()) + } + } + + /** Appends a single element to this buffer. This operation takes constant time. + * + * @param x the element to append. + */ + def += (x: A) { + if (exported) copy() + if (start.isEmpty) { + last0 = new :: (x, Nil) + start = last0 + } else { + val last1 = last0 + last0 = new :: (x, Nil) + last1.tl = last0 + } + } + + /** Clears the buffer contents. + */ + def clear() { + start = Nil + exported = false + } + + /** Prepends a single element to this buffer. This operation takes constant + * time. + * + * @param x the element to prepend. + * @return this buffer. + */ + def +: (x: A): this.type = { + if (exported) copy() + val newElem = new :: (x, start) + if (start.isEmpty) last0 = newElem + start = newElem + this + } + + /** Inserts new elements at the index n. Opposed to method + * update, this method will not replace an element with a new + * one. Instead, it will insert a new element at index n. + * + * @param n the index where a new element will be inserted. + * @param iter the iterable object providing all elements to insert. + * @throws Predef.IndexOutOfBoundsException if n is out of bounds. + */ + def insertAll(n: Int, iter: Iterable[A]) { + try { + if (exported) copy() + var elems = iter.elements.toList.reverse + if (n == 0) { + while (!elems.isEmpty) { + val newElem = new :: (elems.head, start) + if (start.isEmpty) last0 = newElem + start = newElem + elems = elems.tail + } + } else { + var cursor = start + var i = 1 + while (i < n) { + cursor = cursor.tail + i += 1 + } + while (!elems.isEmpty) { + val newElem = new :: (elems.head, cursor.tail) + if (cursor.tail.isEmpty) last0 = newElem + cursor.asInstanceOf[::[A]].tl = newElem + elems = elems.tail + } + } + } catch { + case ex: Exception => + throw new IndexOutOfBoundsException(n.toString()) + } + } + + /** Removes the element on a given index position. This operation takes time linear in + * the buffer size. + * + * @param n the index which refers to the element to delete. + * @return the updated array buffer. + * @throws Predef.IndexOutOfBoundsException if n is out of bounds. + */ + def remove(n: Int, count: Int) { + try { + if (exported) copy() + var old = start.head; + if (n == 0) { + var c = count + while (c > 0) { + start = start.tail + c -= 1 + } + } else { + var cursor = start + var i = 1 + while (i < n) { + cursor = cursor.tail + i += 1 + } + var c = count + while (c > 0) { + if (last0 eq cursor.tail) last0 = cursor.asInstanceOf[::[A]] + cursor.asInstanceOf[::[A]].tl = cursor.tail.tail + c -= 1 + } + } + old + } catch { + case ex: Exception => + throw new IndexOutOfBoundsException(n.toString()) + } + } + +// Implementation of abstract method in Builder + + def result = toList + + /** Converts this buffer to a list. Takes constant time. The buffer is + * copied lazily, the first time it is mutated. + */ + override def toList: List[A] = { + exported = !start.isEmpty + start + } + +// New methods in ListBuffer + + /** Prepends the elements of this buffer to a given list + * + * @param xs the list to which elements are prepended + */ + def prependToList(xs: List[A]): List[A] = + if (start.isEmpty) xs + else { last0.tl = xs; toList } + +// Overrides of methods in Buffer + + /** Removes the element on a given index position. Takes time linear in + * the buffer size (except for the first element, which is removed in constant + * time). + * + * @param n the index which refers to the element to delete. + * @return n the element that was formerly at position n. + * @pre an element exists at position n + * @throws Predef.IndexOutOfBoundsException if n is out of bounds. + */ + override def remove(n: Int): A = try { + if (exported) copy() + var old = start.head; + if (n == 0) { + start = start.tail + } else { + var cursor = start + var i = 1 + while (i < n) { + cursor = cursor.tail + i += 1 + } + old = cursor.tail.head + if (last0 eq cursor.tail) last0 = cursor.asInstanceOf[::[A]] + cursor.asInstanceOf[::[A]].tl = cursor.tail.tail + } + old + } catch { + case ex: Exception => + throw new IndexOutOfBoundsException(n.toString()) + } + + /** Remove a single element from this buffer. This operation takes linear time + * (except removing the first element, which is done in constant time). + * + * @param x the element to remove. + */ + override def -= (x: A) { + if (exported) copy() + if (start.isEmpty) {} + else if (start.head == x) start = start.tail + else { + var cursor = start + while (!cursor.tail.isEmpty && cursor.tail.head != x) { cursor = cursor.tail } + if (!cursor.tail.isEmpty) { + val z = cursor.asInstanceOf[::[A]] + if (z.tl == last0) + last0 = z + z.tl = cursor.tail.tail + } + } + } + + /** expose the underlying list but do not mark it as exported */ + override def readOnly : List[A] = start + + // Private methods + + /** Copy contents of this buffer */ + private def copy() { + var cursor = start + val limit = last0.tail + clear + while (cursor ne limit) { + this += cursor.head + cursor = cursor.tail + } + } + + /** Returns a clone of this buffer. + * + * @return a ListBuffer with the same elements. + */ + override def clone(): Buffer[A] = (new ListBuffer[A]) ++ this + + /** Defines the prefix of the string representation. + * + * @return the string representation of this buffer. + */ + override def stringPrefix: String = "ListBuffer" +} + diff --git a/src/library/scalax/collection/mutable/ResizableArray.scala b/src/library/scalax/collection/mutable/ResizableArray.scala new file mode 100644 index 0000000000..e4dd8bd47d --- /dev/null +++ b/src/library/scalax/collection/mutable/ResizableArray.scala @@ -0,0 +1,103 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2007, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: ResizableArray.scala 15407 2008-06-20 09:26:36Z stepancheg $ + + +package scalax.collection.mutable + +/** This class is used internally to implement data structures that + * are based on resizable arrays. + * + * @author Matthias Zenger, Burak Emir + * @version 1.0, 03/05/2004 + */ +trait ResizableArray[A] extends Vector[A] { + + protected def initialSize: Int = 16 + protected var array: Array[AnyRef] = new Array[AnyRef](initialSize min 1) + + protected var size0: Int = 0 + + //########################################################################## + // implement/override methods of Vector[A] + + /** Returns the length of this resizable array. + */ + def length: Int = size0 + + def apply(idx: Int) = { + if (idx >= size0) throw new IndexOutOfBoundsException(idx.toString) + array(idx).asInstanceOf[A] + } + + def update(idx: Int, elem: A) { + if (idx >= size0) throw new IndexOutOfBoundsException(idx.toString) + array(idx) = elem.asInstanceOf[AnyRef] + } + + /** Fills the given array xs with the elements of + * this sequence starting at position start. + * + * @param xs the array to fill. + * @param start starting index. + */ + override def copyToArray[B >: A](xs: Array[B], start: Int) { + Array.copy(array, 0, xs, start, size0) + } + + /** Copy all elements to a buffer + * @param The buffer to which elements are copied + */ + override def copyToBuffer[B >: A](dest: Buffer[B]) { + dest ++= array.asInstanceOf[Iterable[A]] // !!! + } + + override def foreach(f: A => Unit) { + var i = 0 + while (i < size) { + f(array(i).asInstanceOf[A]) + i += 1 + } + } + + //########################################################################## + + /** remove elements of this array at indices after sz + */ + def reduceToSize(sz: Int) { + require(sz <= size0) + size0 = sz + } + + /** ensure that the internal array has at n cells */ + protected def ensureSize(n: Int) { + if (n > array.length) { + var newsize = array.length * 2 + while (n > newsize) + newsize = newsize * 2 + val newar: Array[AnyRef] = new Array(newsize) + Array.copy(array, 0, newar, 0, size0) + array = newar + } + } + + /** Swap two elements of this array. + */ + protected def swap(a: Int, b: Int) { + val h = array(a) + array(a) = array(b) + array(b) = h + } + + /** Move parts of the array. + */ + protected def copy(m: Int, n: Int, len: Int) { + Array.copy(array, m, array, n, len) + } +} diff --git a/src/library/scalax/collection/mutable/Vector.scala b/src/library/scalax/collection/mutable/Vector.scala new file mode 100644 index 0000000000..b181e93303 --- /dev/null +++ b/src/library/scalax/collection/mutable/Vector.scala @@ -0,0 +1,20 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2006-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: Vector.scala 15437 2008-06-25 16:22:45Z stepancheg $ + +package scalax.collection.mutable + +trait Vector[A] extends collection.Vector[A] with generic.mutable.VectorTemplate[Vector, A] + +object Vector extends generic.nonvariant.SequenceFactory[Vector] { + + /** The empty iterable */ + def apply[A](args: A*): Vector[A] = null // !!! + +} diff --git a/src/library/scalax/util/control/Break.scala b/src/library/scalax/util/control/Break.scala new file mode 100755 index 0000000000..173188d2e9 --- /dev/null +++ b/src/library/scalax/util/control/Break.scala @@ -0,0 +1,15 @@ +package scalax.util.control + +object Break { + private class BreakException extends RuntimeException + private val breakException = new BreakException + def break { throw breakException } + def breakable(op: => Unit) { + try { + op + } catch { + case ex: BreakException => + } + } +} + diff --git a/test/files/neg/accesses.check b/test/files/neg/accesses.check index 627c17dd90..94fcd615bc 100644 --- a/test/files/neg/accesses.check +++ b/test/files/neg/accesses.check @@ -1,16 +1,16 @@ -accesses.scala:23: error: error overriding method f2 in class A of type => Unit; +accesses.scala:23: error: overriding method f2 in class A of type => Unit; method f2 has weaker access privileges; it should not be private private def f2: Unit = () ^ -accesses.scala:24: error: error overriding method f3 in class A of type => Unit; +accesses.scala:24: error: overriding method f3 in class A of type => Unit; method f3 has weaker access privileges; it should be at least protected private[p2] def f3: Unit = () ^ -accesses.scala:25: error: error overriding method f4 in class A of type => Unit; +accesses.scala:25: error: overriding method f4 in class A of type => Unit; method f4 has weaker access privileges; it should be at least private[p1] private[p2] def f4: Unit ^ -accesses.scala:26: error: error overriding method f5 in class A of type => Unit; +accesses.scala:26: error: overriding method f5 in class A of type => Unit; method f5 has weaker access privileges; it should be at least protected[p1] protected[p2] def f5: Unit ^ diff --git a/test/files/neg/bug521.check b/test/files/neg/bug521.check index b85e26677b..eda06ff9f6 100644 --- a/test/files/neg/bug521.check +++ b/test/files/neg/bug521.check @@ -1,14 +1,14 @@ bug521.scala:10: error: class PlainFile needs to be abstract, since method path in class AbstractFile of type => String is not defined class PlainFile(val file : File) extends AbstractFile {} ^ -bug521.scala:13: error: error overriding value file in class PlainFile of type java.io.File; +bug521.scala:13: error: overriding value file in class PlainFile of type java.io.File; value file needs `override' modifier final class ZipArchive(val file : File, archive : ZipFile) extends PlainFile(file) { ^ bug521.scala:13: error: class ZipArchive needs to be abstract, since method path in class AbstractFile of type => String is not defined final class ZipArchive(val file : File, archive : ZipFile) extends PlainFile(file) { ^ -bug521.scala:15: error: error overriding value path in class VirtualFile of type String; +bug521.scala:15: error: overriding value path in class VirtualFile of type String; method path is not stable override def path = ""; ^ diff --git a/test/files/neg/bug630.check b/test/files/neg/bug630.check index 03113be7da..739d214fe5 100644 --- a/test/files/neg/bug630.check +++ b/test/files/neg/bug630.check @@ -1,5 +1,5 @@ -bug630.scala:20: error: error overriding value foo in trait Bar of type Req2; - object foo has incompatible type object Test.foo +bug630.scala:20: error: overriding value foo in trait Bar of type Req2; + object foo has incompatible type object foo extends Req1 ^ one error found diff --git a/test/files/neg/bug708.check b/test/files/neg/bug708.check index 513709f2a0..cfeb01c87f 100644 --- a/test/files/neg/bug708.check +++ b/test/files/neg/bug708.check @@ -1,5 +1,5 @@ -bug708.scala:8: error: error overriding type S in trait X with bounds >: Nothing <: A.this.T; - type S has incompatible type Any +bug708.scala:8: error: overriding type S in trait X with bounds >: Nothing <: A.this.T; + type S has incompatible type override private[A] type S = Any; ^ one error found diff --git a/test/files/neg/lazy-override.check b/test/files/neg/lazy-override.check index d1c9d305f0..793e6b2020 100644 --- a/test/files/neg/lazy-override.check +++ b/test/files/neg/lazy-override.check @@ -1,8 +1,8 @@ -lazy-override.scala:11: error: error overriding value x in class A of type Int; +lazy-override.scala:11: error: overriding value x in class A of type Int; lazy value x cannot override a concrete non-lazy value override lazy val x: Int = { print("/*B.x*/"); 3 } ^ -lazy-override.scala:13: error: error overriding lazy value y in class A of type Int; +lazy-override.scala:13: error: overriding lazy value y in class A of type Int; value y must be declared lazy to override a concrete lazy value override val y: Int = { print("/*B.y*/"); 3 } ^ diff --git a/test/files/neg/t1163.check b/test/files/neg/t1163.check index c96ec732a5..69e6b7ac4a 100644 --- a/test/files/neg/t1163.check +++ b/test/files/neg/t1163.check @@ -1,5 +1,5 @@ -t1163.scala:2: error: error overriding method foo in trait Sub of type => Sub; - method foo in trait Super of type => Super has incompatible type => Super; +t1163.scala:2: error: overriding method foo in trait Sub of type => Sub; + method foo in trait Super of type => Super has incompatible type; (Note that method foo in trait Sub of type => Sub is abstract, and is therefore overridden by concrete method foo in trait Super of type => Super) trait Sub extends Super { override def foo: Sub } diff --git a/test/files/neg/tcpoly_variance.check b/test/files/neg/tcpoly_variance.check index 146240ac9b..2df4b4d429 100644 --- a/test/files/neg/tcpoly_variance.check +++ b/test/files/neg/tcpoly_variance.check @@ -1,5 +1,5 @@ -tcpoly_variance.scala:6: error: error overriding method str in class A of type => m[java.lang.Object]; - method str has incompatible type => m[String] +tcpoly_variance.scala:6: error: overriding method str in class A of type => m[java.lang.Object]; + method str has incompatible type override def str: m[String] = error("foo") // since x in m[x] is invariant, ! m[String] <: m[Object] ^ one error found diff --git a/test/files/run/t1524.check b/test/files/run/t1524.check new file mode 100644 index 0000000000..e79c5e8f96 --- /dev/null +++ b/test/files/run/t1524.check @@ -0,0 +1 @@ +initial diff --git a/test/files/run/t1524.scala b/test/files/run/t1524.scala new file mode 100644 index 0000000000..4520028dda --- /dev/null +++ b/test/files/run/t1524.scala @@ -0,0 +1,7 @@ +object Test extends Application { + + val buf = new scala.collection.mutable.ArrayBuffer[String] { override val initialSize = 0 } + buf += "initial" + buf += "second" + println(buf.first) +} \ No newline at end of file -- cgit v1.2.3