From 5b2dec1e9ea1a41112d87a37a16811b71bdaf273 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 26 May 2009 18:18:11 +0000 Subject: new test repl for interactive mode; bug fixes --- .../scala/tools/nsc/interactive/ContextTrees.scala | 46 ++++++++---- .../scala/tools/nsc/interactive/Global.scala | 11 +-- .../scala/tools/nsc/interactive/REPL.scala | 86 ++++++++++------------ .../tools/nsc/reporters/AbstractReporter.scala | 7 +- .../scala/tools/nsc/typechecker/Implicits.scala | 30 ++++---- src/compiler/scala/tools/nsc/util/Position.scala | 4 + 6 files changed, 98 insertions(+), 86 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala b/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala index 8f8470ae2d..8e8d280b4b 100755 --- a/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala +++ b/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala @@ -50,23 +50,37 @@ trait ContextTrees { self: Global => else if (cpos precedes contexts(0).pos) new ContextTree(context) +: contexts else { + def insertAt(idx: Int): Boolean = { + val oldpos = contexts(idx).pos + if (oldpos sameRange cpos) { + contexts(idx) = new ContextTree(context, contexts(idx).children) + true + } else if (oldpos includes cpos) { + addContext(contexts(idx).children, context) + true + } else if (cpos includes oldpos) { + val start = contexts.indexWhere(cpos includes _.pos) + val last = contexts.lastIndexWhere(cpos includes _.pos) + contexts(start) = new ContextTree(context, contexts.slice(start, last + 1)) + contexts.remove(start + 1, last - start) + true + } else false + } def loop(lo: Int, hi: Int) { - assert(lo <= hi) - val mid = (lo + hi) / 2 - val midpos = contexts(mid).pos - if (cpos precedes midpos) - loop(lo, mid - 1) - else if (midpos precedes cpos) - loop(mid + 1, hi) - else if (midpos sameRange cpos) - contexts(mid) = new ContextTree(context, contexts(mid).children) - else if ((midpos includes cpos) && !(cpos includes midpos)) - addContext(contexts(mid).children, context) - else if (cpos includes midpos) - contexts(mid) = new ContextTree(context, ArrayBuffer(contexts(mid))) - else { - inform("internal error? skewed positions: "+midpos+"/"+cpos) - contexts(mid) = new ContextTree(context) + if (hi - lo > 1) { + val mid = (lo + hi) / 2 + val midpos = contexts(mid).pos + if (cpos precedes midpos) + loop(lo, mid) + else if (midpos precedes cpos) + loop(mid, hi) + } else if (!insertAt(lo) && !insertAt(hi)) { + val lopos = contexts(lo).pos + val hipos = contexts(hi).pos + if ((lopos precedes cpos) && (cpos precedes hipos)) + contexts.insert(hi, new ContextTree(context)) + else + inform("internal error? skewed positions: "+lopos+" !< "+cpos+" !< "+hipos) } } loop(0, hi) diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala index 16af234d48..3d0d14293d 100755 --- a/src/compiler/scala/tools/nsc/interactive/Global.scala +++ b/src/compiler/scala/tools/nsc/interactive/Global.scala @@ -55,7 +55,7 @@ self => def integrateNew() { context.unit.body = new TreeReplacer(old, result) transform context.unit.body } - if (context.unit.targetPos includes result.pos) { + if ((context.unit != null) && (context.unit.targetPos includes result.pos)) { integrateNew() throw new TyperResult(result) } @@ -233,11 +233,12 @@ self => override def traverse(t: Tree) { if (t.hasSymbol) t.symbol = NoSymbol t match { - case EmptyTree => ; + case EmptyTree => + ; + case _ => + t.tpe = null + super.traverse(t) } - - t.tpe = null - super.traverse(t) } } diff --git a/src/compiler/scala/tools/nsc/interactive/REPL.scala b/src/compiler/scala/tools/nsc/interactive/REPL.scala index cd83ddf082..11def170b5 100644 --- a/src/compiler/scala/tools/nsc/interactive/REPL.scala +++ b/src/compiler/scala/tools/nsc/interactive/REPL.scala @@ -1,15 +1,20 @@ package scala.tools.nsc.interactive import scala.concurrent.SyncVar -import scala.tools.nsc.io.AbstractFile -import scala.tools.nsc.util.{SourceFile, Position, WorkScheduler} +import scala.tools.nsc.util._ import scala.tools.nsc.symtab._ import scala.tools.nsc.ast._ +import scala.tools.nsc.reporters._ +import scala.tools.nsc.io._ /** Interface of interactive compiler to a client such as an IDE */ -object REPL /* extends EvalLoop */ { -} +object REPL extends EvalLoop { + + val settings = new Settings() + val comp = new Global(settings, new ConsoleReporter(settings)) + + def prompt = "> " /** Commands: * @@ -17,56 +22,39 @@ object REPL /* extends EvalLoop */ { * typeat file line col * * + */ def run() { + val reloadResult = new SyncVar[Either[Unit, Throwable]] + val typeatResult = new SyncVar[Either[comp.Tree, Throwable]] loop { line => - - + (line split " ").toList match { + case "reload" :: args => + comp.askReload(args map toSourceFile, reloadResult) + show(reloadResult) + case List("typeat", file, line, col1, col2) => + val source = toSourceFile(file) + val linestart = source.lineToOffset(line.toInt) + val pos = comp.rangePos(source, linestart + col1.toInt, linestart + col1.toInt, linestart + col2.toInt) + comp.askTypeAt(pos, typeatResult) + show(typeatResult) + case List("quit") => + System.exit(1) + case _ => + println("unrecongized command") + } } + } - def process(args: Array[String]) { - val settings = new Settings(error) - reporter = new ConsoleReporter(settings) - val command = new CompilerCommand(List.fromArray(args), settings, error, false) - if (command.settings.version.value) - reporter.info(null, versionMsg, true) - else { - if (command.settings.target.value == "msil") { - val libpath = System.getProperty("msil.libpath") - if (libpath != null) - command.settings.assemrefs.value = - command.settings.assemrefs.value + File.pathSeparator + libpath - } - try { - object compiler extends Global(command.settings, reporter) - if (reporter.hasErrors) { - reporter.flush() - return - } + def toSourceFile(name: String) = new BatchSourceFile(new PlainFile(new java.io.File(name))) - if (command.shouldStopWithInfo) { - reporter.info(null, command.getInfoMessage(compiler), true) - } else { - if (command.settings.resident.value) - resident(compiler) - else if (command.files.isEmpty) { - reporter.info(null, command.usageMsg, true) - reporter.info(null, compiler.pluginOptionsHelp, true) - } else { - val run = new compiler.Run() - run compile command.files - reporter.printSummary() - } - } - } catch { - case ex @ FatalError(msg) => - if (true || command.settings.debug.value) // !!! - ex.printStackTrace(); - reporter.error(null, "fatal error: " + msg) - } + def show[T](svar: SyncVar[Either[T, Throwable]]) { + svar.get match { + case Left(result) => println("==> "+result) + case Right(exc/*: Throwable ??*/) => exc.printStackTrace; println("ERROR: "+exc) } } - val comp = new Global - - def - */ + def main(args: Array[String]) { + run() + } +} diff --git a/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala b/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala index d6f94e8626..5aedf09a28 100644 --- a/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala +++ b/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala @@ -44,6 +44,7 @@ abstract class AbstractReporter extends Reporter { } /** Logs a position and returns true if it was already logged. + * @note Two positions are considered identical for logging if they have the same point. * * @param pos ... * @return true if pos was already logged. @@ -51,9 +52,9 @@ abstract class AbstractReporter extends Reporter { private def testAndLog(pos: Position, severity: Severity): Boolean = { if (pos eq null) return false if (pos.offset.isEmpty) return false - if ((positions contains pos) && positions(pos) >= severity) return true - positions += (pos -> severity) + val fpos = pos.focus + if ((positions contains fpos) && positions(fpos) >= severity) return true + positions += (fpos -> severity) false } - } diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 667827dfa4..43f6d54be0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -28,7 +28,7 @@ self: Analyzer => import definitions._ import posAssigner.atPos - final val traceImplicits = false + final val traceImplicits = true var implicitTime = 0L var inscopeSucceed = 0L @@ -55,6 +55,7 @@ self: Analyzer => * @return A search result */ def inferImplicit(tree: Tree, pt0: Type, reportAmbiguous: Boolean, context: Context): SearchResult = { + println("infer impl "+pt0) if (traceImplicits && !tree.isEmpty && !context.undetparams.isEmpty) println("typing implicit with undetermined type params: "+context.undetparams+"\n"+tree) val search = new ImplicitSearch(tree, pt0, context.makeImplicit(reportAmbiguous)) @@ -302,7 +303,7 @@ self: Analyzer => */ val wildPt = approximate(pt) - //if (traceImplicits) println("typed impl for "+wildPt+"? "+info.name+":"+info.tpe+"/"+undetParams) + if (traceImplicits) println("typed impl for "+wildPt+"? "+info.name+":"+info.tpe+"/"+undetParams) if (isPlausiblyCompatible(info.tpe, wildPt) && isCompatible(depoly(info.tpe), wildPt) && isStable(info.pre)) { @@ -311,7 +312,7 @@ self: Analyzer => if (info.pre == NoPrefix) Ident(info.name) else Select(gen.mkAttributedQualifier(info.pre), info.name) } - //if (traceImplicits) println("typed impl?? "+info.name+":"+info.tpe+" ==> "+itree+" with "+wildPt) + if (traceImplicits) println("typed impl?? "+info.name+":"+info.tpe+" ==> "+itree+" with "+wildPt) def fail(reason: String): SearchResult = { if (settings.XlogImplicits.value) inform(itree+" is not a valid implicit value for "+pt0+" because:\n"+reason) @@ -653,18 +654,21 @@ self: Analyzer => //val timer1 = System.nanoTime() //if (result == SearchFailure) inscopeFail += timer1 - start else inscopeSucceed += timer1 - start if (result == SearchFailure) { - implicitsCache get pt match { - case Some(r) => - hits += 1 - result = r - case None => - misses += 1 - result = searchImplicit(implicitsOfExpectedType, false) -// println("new fact: search implicit of "+pt+" = "+result) + if (pt.isInstanceOf[UniqueType]) + implicitsCache get pt match { + case Some(r) => + hits += 1 + result = r + case None => + misses += 1 + result = searchImplicit(implicitsOfExpectedType, false) +// println("new fact: search implicit of "+pt+" = "+result) // if (implicitsCache.size >= sizeLimit) // implicitsCache -= implicitsCache.values.next - implicitsCache(pt) = result - } + implicitsCache(pt) = result + } + else + result = searchImplicit(implicitsOfExpectedType, false) } //val timer2 = System.nanoTime() //if (result == SearchFailure) oftypeFail += timer2 - timer1 else oftypeSucceed += timer2 - timer1 diff --git a/src/compiler/scala/tools/nsc/util/Position.scala b/src/compiler/scala/tools/nsc/util/Position.scala index a62974c426..1ebc2df19e 100644 --- a/src/compiler/scala/tools/nsc/util/Position.scala +++ b/src/compiler/scala/tools/nsc/util/Position.scala @@ -27,10 +27,13 @@ trait Position { def endOrElse(d: Int) = offset.getOrElse(d) def underlying = this + def focus = this def includes(pos: Position) = isDefined && pos.isDefined && start <= pos.start && pos.end <= end + /** Does this position precede that position? + */ def precedes(pos: Position) = isDefined && pos.isDefined && end <= pos.start @@ -114,6 +117,7 @@ extends OffsetPosition(source0, point) { override def startOrElse(d: Int) = start override def pointOrElse(d: Int) = point override def endOrElse(d: Int) = end + override def focus = OffsetPosition(source0, point) } -- cgit v1.2.3