diff --git a/build.sbt b/build.sbt index a27722235ee1..c2c5fda85b36 100644 --- a/build.sbt +++ b/build.sbt @@ -18,6 +18,7 @@ val `dotty-bench-bootstrapped` = Build.`dotty-bench-bootstrapped` val `scala-library` = Build.`scala-library` val `scala-compiler` = Build.`scala-compiler` val `scala-reflect` = Build.`scala-reflect` +val `dotty-repl` = Build.`dotty-repl` val scalap = Build.scalap val dist = Build.dist val `dist-bootstrapped` = Build.`dist-bootstrapped` diff --git a/compiler/src/dotty/tools/dotc/Bench.scala b/compiler/src/dotty/tools/dotc/Bench.scala index 56b6dabbe4ca..ee7789c32f9f 100644 --- a/compiler/src/dotty/tools/dotc/Bench.scala +++ b/compiler/src/dotty/tools/dotc/Bench.scala @@ -16,8 +16,6 @@ object Bench extends Driver { @sharable private var numRuns = 1 - def newCompiler(implicit ctx: Context): Compiler = new Compiler - private def ntimes(n: Int)(op: => Reporter): Reporter = (emptyReporter /: (0 until n)) ((_, _) => op) diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 84d49502dc54..a6555cdcd50e 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -116,34 +116,6 @@ class Compiler { runId += 1; runId } - /** Produces the following contexts, from outermost to innermost - * - * bootStrap: A context with next available runId and a scope consisting of - * the RootPackage _root_ - * start A context with RootClass as owner and the necessary initializations - * for type checking. - * imports For each element of RootImports, an import context - */ - def rootContext(implicit ctx: Context): Context = { - ctx.initialize()(ctx) - ctx.setPhasePlan(phases) - val rootScope = new MutableScope - val bootstrap = ctx.fresh - .setPeriod(Period(nextRunId, FirstPhaseId)) - .setScope(rootScope) - rootScope.enter(ctx.definitions.RootPackage)(bootstrap) - val start = bootstrap.fresh - .setOwner(defn.RootClass) - .setTyper(new Typer) - .addMode(Mode.ImplicitsEnabled) - .setTyperState(new MutableTyperState(ctx.typerState, ctx.typerState.reporter, isCommittable = true)) - .setFreshNames(new FreshNameCreator.Default) - ctx.initialize()(start) // re-initialize the base context with start - def addImport(ctx: Context, refFn: () => TermRef) = - ctx.fresh.setImportInfo(ImportInfo.rootImport(refFn)(ctx)) - (start.setRunInfo(new RunInfo(start)) /: defn.RootImportFns)(addImport) - } - def reset()(implicit ctx: Context): Unit = { ctx.base.reset() ctx.runInfo.clear() @@ -151,6 +123,6 @@ class Compiler { def newRun(implicit ctx: Context): Run = { reset() - new Run(this)(rootContext) + new Run(this, ctx) } } diff --git a/compiler/src/dotty/tools/dotc/Driver.scala b/compiler/src/dotty/tools/dotc/Driver.scala index f54a23ad2451..8e2c1910dcd6 100644 --- a/compiler/src/dotty/tools/dotc/Driver.scala +++ b/compiler/src/dotty/tools/dotc/Driver.scala @@ -13,9 +13,9 @@ import scala.util.control.NonFatal * process, but in most cases you only need to call [[process]] on the * existing object [[Main]]. */ -abstract class Driver extends DotClass { +class Driver extends DotClass { - protected def newCompiler(implicit ctx: Context): Compiler + protected def newCompiler(implicit ctx: Context): Compiler = new Compiler protected def emptyReporter: Reporter = new StoreReporter(null) diff --git a/compiler/src/dotty/tools/dotc/FromTasty.scala b/compiler/src/dotty/tools/dotc/FromTasty.scala index 6f74a5d46ea2..2cb0aa79456a 100644 --- a/compiler/src/dotty/tools/dotc/FromTasty.scala +++ b/compiler/src/dotty/tools/dotc/FromTasty.scala @@ -45,11 +45,11 @@ object FromTasty extends Driver { override def newRun(implicit ctx: Context): Run = { reset() - new TASTYRun(this)(rootContext) + new TASTYRun(this, ctx) } } - class TASTYRun(comp: Compiler)(implicit ctx: Context) extends Run(comp) { + class TASTYRun(comp: Compiler, ictx: Context) extends Run(comp, ictx) { override def compile(classNames: List[String]) = { units = classNames.map(new TASTYCompilationUnit(_)) compileUnits() diff --git a/compiler/src/dotty/tools/dotc/Main.scala b/compiler/src/dotty/tools/dotc/Main.scala index a6844fbbc4c0..5ba3f35b0cc9 100644 --- a/compiler/src/dotty/tools/dotc/Main.scala +++ b/compiler/src/dotty/tools/dotc/Main.scala @@ -4,6 +4,4 @@ package dotc import core.Contexts.Context /** Main class of the `dotc` batch compiler. */ -object Main extends Driver { - override def newCompiler(implicit ctx: Context): Compiler = new Compiler -} +object Main extends Driver diff --git a/compiler/src/dotty/tools/dotc/Resident.scala b/compiler/src/dotty/tools/dotc/Resident.scala index 56f6684d0964..7f6c288cc892 100644 --- a/compiler/src/dotty/tools/dotc/Resident.scala +++ b/compiler/src/dotty/tools/dotc/Resident.scala @@ -27,8 +27,6 @@ class Resident extends Driver { object residentCompiler extends Compiler - override def newCompiler(implicit ctx: Context): Compiler = ??? - override def sourcesRequired = false private val quit = ":q" diff --git a/compiler/src/dotty/tools/dotc/Run.scala b/compiler/src/dotty/tools/dotc/Run.scala index e9ec9dd3d96b..f8e7967e89d8 100644 --- a/compiler/src/dotty/tools/dotc/Run.scala +++ b/compiler/src/dotty/tools/dotc/Run.scala @@ -6,6 +6,9 @@ import Contexts._ import Periods._ import Symbols._ import Phases._ +import Types._ +import Scopes._ +import typer.{FrontEnd, Typer, ImportInfo, RefChecks} import Decorators._ import dotty.tools.dotc.transform.TreeTransforms.TreeTransformer import io.PlainFile @@ -22,12 +25,41 @@ import dotty.tools.io.VirtualFile import scala.util.control.NonFatal /** A compiler run. Exports various methods to compile source files */ -class Run(comp: Compiler)(implicit ctx: Context) { - - assert(ctx.runId <= Periods.MaxPossibleRunId) +class Run(comp: Compiler, ictx: Context) { var units: List[CompilationUnit] = _ + /** Produces the following contexts, from outermost to innermost + * + * bootStrap: A context with next available runId and a scope consisting of + * the RootPackage _root_ + * start A context with RootClass as owner and the necessary initializations + * for type checking. + * imports For each element of RootImports, an import context + */ + protected[this] def rootContext(implicit ctx: Context): Context = { + ctx.initialize()(ctx) + ctx.setPhasePlan(comp.phases) + val rootScope = new MutableScope + val bootstrap = ctx.fresh + .setPeriod(Period(comp.nextRunId, FirstPhaseId)) + .setScope(rootScope) + rootScope.enter(ctx.definitions.RootPackage)(bootstrap) + val start = bootstrap.fresh + .setOwner(defn.RootClass) + .setTyper(new Typer) + .addMode(Mode.ImplicitsEnabled) + .setTyperState(new MutableTyperState(ctx.typerState, ctx.typerState.reporter, isCommittable = true)) + .setFreshNames(new FreshNameCreator.Default) + ctx.initialize()(start) // re-initialize the base context with start + def addImport(ctx: Context, refFn: () => TermRef) = + ctx.fresh.setImportInfo(ImportInfo.rootImport(refFn)(ctx)) + (start.setRunInfo(new RunInfo(start)) /: defn.RootImportFns)(addImport) + } + + protected[this] implicit val ctx: Context = rootContext(ictx) + assert(ctx.runId <= Periods.MaxPossibleRunId) + def getSource(fileName: String): SourceFile = { val f = new PlainFile(fileName) if (f.isDirectory) { @@ -63,7 +95,17 @@ class Run(comp: Compiler)(implicit ctx: Context) { compileUnits() } - protected def compileUnits() = Stats.monitorHeartBeat { + def compileUnits(us: List[CompilationUnit]): Unit = { + units = us + compileUnits() + } + + def compileUnits(us: List[CompilationUnit], ctx: Context): Unit = { + units = us + compileUnits()(ctx) + } + + protected def compileUnits()(implicit ctx: Context) = Stats.monitorHeartBeat { ctx.checkSingleThreaded() // If testing pickler, make sure to stop after pickling phase: @@ -76,7 +118,7 @@ class Run(comp: Compiler)(implicit ctx: Context) { ctx.usePhases(phases) var lastPrintedTree: PrintedTree = NoPrintedTree for (phase <- ctx.allPhases) - if (!ctx.reporter.hasErrors) { + if (phase.isRunnable) { val start = System.currentTimeMillis units = phase.runOn(units) if (ctx.settings.Xprint.value.containsPhase(phase)) { diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 62d8d57f6f47..561ee6a00746 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -251,7 +251,7 @@ class Definitions { lazy val Any_getClass = enterMethod(AnyClass, nme.getClass_, MethodType(Nil, ClassClass.typeRef.appliedTo(TypeBounds.empty)), Final) lazy val Any_isInstanceOf = enterT1ParameterlessMethod(AnyClass, nme.isInstanceOf_, _ => BooleanType, Final) lazy val Any_asInstanceOf = enterT1ParameterlessMethod(AnyClass, nme.asInstanceOf_, TypeParamRef(_, 0), Final) - lazy val Any_typeTest = enterT1ParameterlessMethod(AnyClass, nme.isInstanceOfPM, _ => BooleanType, Final) + lazy val Any_typeTest = enterT1ParameterlessMethod(AnyClass, nme.isInstanceOfPM, _ => BooleanType, Final | Synthetic) // generated by pattern matcher, eliminated by erasure def AnyMethods = List(Any_==, Any_!=, Any_equals, Any_hashCode, @@ -863,8 +863,8 @@ class Definitions { ) val PredefImportFns = List[() => TermRef]( - () => ScalaPredefModuleRef, - () => DottyPredefModuleRef + () => ScalaPredefModuleRef, + () => DottyPredefModuleRef ) lazy val RootImportFns = diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala index bb50c1928895..3377ef0bda23 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -64,7 +64,8 @@ object NameOps { def isConstructorName = name == CONSTRUCTOR || name == TRAIT_CONSTRUCTOR def isStaticConstructorName = name == STATIC_CONSTRUCTOR def isLocalDummyName = name startsWith str.LOCALDUMMY_PREFIX - def isReplWrapperName = name.toString contains str.INTERPRETER_IMPORT_WRAPPER + def isReplWrapperName = name.toString contains str.REPL_SESSION_LINE + def isReplAssignName = name.toString contains str.REPL_ASSIGN_SUFFIX def isSetterName = name endsWith str.SETTER_SUFFIX def isScala2LocalSuffix = testSimple(_.endsWith(" ")) def isSelectorName = testSimple(n => n.startsWith("_") && n.drop(1).forall(_.isDigit)) diff --git a/compiler/src/dotty/tools/dotc/core/Phases.scala b/compiler/src/dotty/tools/dotc/core/Phases.scala index 409827f39ad6..0312f94f4533 100644 --- a/compiler/src/dotty/tools/dotc/core/Phases.scala +++ b/compiler/src/dotty/tools/dotc/core/Phases.scala @@ -272,11 +272,16 @@ object Phases { */ def phaseName: String + def isRunnable(implicit ctx: Context): Boolean = + !ctx.reporter.hasErrors + /** List of names of phases that should precede this phase */ def runsAfter: Set[Class[_ <: Phase]] = Set.empty + /** @pre `isRunnable` returns true */ def run(implicit ctx: Context): Unit + /** @pre `isRunnable` returns true */ def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] = units.map { unit => val unitCtx = ctx.fresh.setPhase(this.start).setCompilationUnit(unit) diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 3d90d7f4a220..312ebcf1b5f7 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -30,10 +30,10 @@ object StdNames { final val ANON_CLASS = "$anon" final val ANON_FUN = "$anonfun" - final val INTERPRETER_IMPORT_WRAPPER = "$iw" - final val INTERPRETER_LINE_PREFIX = "line" - final val INTERPRETER_VAR_PREFIX = "res" - final val INTERPRETER_WRAPPER_SUFFIX = "$object" + final val REPL_SESSION_LINE = "rs$line$" + final val REPL_ASSIGN_SUFFIX = "$assign" + final val REPL_RES_PREFIX = "res" + final val MODULE_INSTANCE_FIELD = "MODULE$" final val Function = "Function" diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index ce738357ea39..473ae32f9dc1 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1994,7 +1994,9 @@ object SymDenotations { } def isValidAt(phase: Phase)(implicit ctx: Context) = - createdAt.runId == ctx.runId && sameGroup(ctx.phases(createdAt.phaseId), phase) + createdAt.runId == ctx.runId && + createdAt.phaseId < ctx.phases.length && + sameGroup(ctx.phases(createdAt.phaseId), phase) } private class InvalidCache extends InheritedCache { diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 724363be41d4..32ce37b10a9f 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -154,6 +154,14 @@ object Types { false } + def isInfixType(implicit ctx: Context): Boolean = this match { + case TypeApplications.AppliedType(tycon, args) => + args.length == 2 && + !Character.isUnicodeIdentifierStart(tycon.typeSymbol.name.toString.head) + // TODO: Once we use the 2.12 stdlib, also check the @showAsInfix annotation + case _ => false + } + /** Does this type refer exactly to class symbol `sym`, instead of to a subclass of `sym`? * Implemented like `isRef`, but follows more types: all type proxies as well as and- and or-types */ diff --git a/compiler/src/dotty/tools/dotc/interactive/Interactive.scala b/compiler/src/dotty/tools/dotc/interactive/Interactive.scala index 4284da2967a9..fc6def3190ee 100644 --- a/compiler/src/dotty/tools/dotc/interactive/Interactive.scala +++ b/compiler/src/dotty/tools/dotc/interactive/Interactive.scala @@ -65,35 +65,51 @@ object Interactive { private def safely[T](op: => List[T]): List[T] = try op catch { case ex: TypeError => Nil } - /** Possible completions at position `pos` */ - def completions(trees: List[SourceTree], pos: SourcePosition)(implicit ctx: Context): List[Symbol] = { + /** Get possible completions from tree at `pos` + * + * @return offset and list of symbols for possible completions + */ + def completions(trees: List[SourceTree], pos: SourcePosition)(implicit ctx: Context): (Int, List[Symbol]) = { val path = pathTo(trees, pos) val boundary = enclosingDefinitionInPath(path).symbol - path.take(1).flatMap { - case Select(qual, _) => + // FIXME: Get all declarations available in the current scope, not just + // those from the enclosing class + def scopeCompletions: List[Symbol] = + boundary.enclosingClass match { + case csym: ClassSymbol => + val classRef = csym.classInfo.typeRef + completions(classRef, boundary) + case _ => + Nil + } + + path.headOption.map { + case sel @ Select(qual, name) => // When completing "`a.foo`, return the members of `a` - completions(qual.tpe, boundary) + (sel.pos.point, completions(qual.tpe, boundary)) + case id @ Ident(name) => + (id.pos.point, scopeCompletions) case _ => - // FIXME: Get all declarations available in the current scope, not just - // those from the enclosing class - boundary.enclosingClass match { - case csym: ClassSymbol => - val classRef = csym.classInfo.typeRef - completions(classRef, boundary) - case _ => - Nil - } + (0, scopeCompletions) } + .getOrElse((0, Nil)) } /** Possible completions of members of `prefix` which are accessible when called inside `boundary` */ def completions(prefix: Type, boundary: Symbol)(implicit ctx: Context): List[Symbol] = safely { - val boundaryCtx = ctx.withOwner(boundary) - prefix.memberDenots(completionsFilter, (name, buf) => - buf ++= prefix.member(name).altsWith(d => !d.isAbsent && d.symbol.isAccessibleFrom(prefix)(boundaryCtx)) - ).map(_.symbol).toList + if (boundary != NoSymbol) { + val boundaryCtx = ctx.withOwner(boundary) + prefix.memberDenots(completionsFilter, (name, buf) => + buf ++= prefix.member(name).altsWith{ d => + !d.isAbsent && + !d.is(Synthetic) && !d.is(Artifact) && + d.symbol.isAccessibleFrom(prefix)(boundaryCtx) + } + ).map(_.symbol).toList + } + else Nil } /** Filter for names that should appear when looking for completions. */ diff --git a/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala b/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala index a27db1d5d5db..14ab4046d702 100644 --- a/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala +++ b/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala @@ -28,8 +28,6 @@ class InteractiveDriver(settings: List[String]) extends Driver { import tpd._ import InteractiveDriver._ - // FIXME: Change the Driver API to not require implementing this method - override protected def newCompiler(implicit ctx: Context): Compiler = ??? override def sourcesRequired = false private val myInitCtx: Context = { diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index c494110201e4..f5e99390f1df 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -125,14 +125,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { ("implicit " provided isImplicit) ~ argStr ~ " => " ~ argText(args.last) } - def isInfixType(tp: Type): Boolean = tp match { - case AppliedType(tycon, args) => - args.length == 2 && - !Character.isUnicodeIdentifierStart(tycon.typeSymbol.name.toString.head) - // TODO: Once we use the 2.12 stdlib, also check the @showAsInfix annotation - case _ => - false - } def toTextInfixType(op: Type, args: List[Type]): Text = { /* SLS 3.2.8: all infix types have the same precedence. * In A op B op' C, op and op' need the same associativity. @@ -140,8 +132,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { * needs to be parenthesized if it's an infix type, and vice versa. */ val l :: r :: Nil = args val isRightAssoc = op.typeSymbol.name.endsWith(":") - val leftArg = if (isRightAssoc && isInfixType(l)) "(" ~ toText(l) ~ ")" else toText(l) - val rightArg = if (!isRightAssoc && isInfixType(r)) "(" ~ toText(r) ~ ")" else toText(r) + val leftArg = if (isRightAssoc && l.isInfixType) "(" ~ toText(l) ~ ")" else toText(l) + val rightArg = if (!isRightAssoc && r.isInfixType) "(" ~ toText(r) ~ ")" else toText(r) leftArg ~ " " ~ toTextLocal(op) ~ " " ~ rightArg } @@ -154,7 +146,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { if (tycon.isRepeatedParam) return toTextLocal(args.head) ~ "*" if (defn.isFunctionClass(cls)) return toTextFunction(args, cls.name.isImplicitFunction) if (defn.isTupleClass(cls)) return toTextTuple(args) - if (isInfixType(tp)) return toTextInfixType(tycon, args) + if (tp.isInfixType) return toTextInfixType(tycon, args) case EtaExpansion(tycon) => return toText(tycon) case tp: TypeRef => diff --git a/compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala b/compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala index eb88cb64f766..759b4d9cfc8e 100644 --- a/compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala +++ b/compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala @@ -76,7 +76,7 @@ object SyntaxHighlighting { if (remaining.nonEmpty) takeChar() // drop 1 for appendLiteral appendString('"', next == "\"\"\"") } else { - if (n.isUpper && keywordStart) { + if (n.isUpper && (keywordStart || prev == '.')) { appendWhile(n, !typeEnders.contains(_), typeDef) } else if (keywordStart) { append(n, keywords.contains(_), { kw => @@ -125,7 +125,8 @@ object SyntaxHighlighting { takeChars(2) newBuf append tripleQs prev = '?' - } else if (n.isUpper && keywordStart) + } + else if (n.isUpper && keywordStart) appendWhile(n, !typeEnders.contains(_), typeDef) else if (numberStart(n)) appendWhile(n, { x => x.isDigit || x == '.' || x == '\u0000'}, literal) @@ -281,6 +282,7 @@ object SyntaxHighlighting { case '[' => true case ':' => true case '@' => true + case '.' => true case _ => false } diff --git a/compiler/src/dotty/tools/dotc/repl/AmmoniteReader.scala b/compiler/src/dotty/tools/dotc/repl/AmmoniteReader.scala deleted file mode 100644 index f3b68e4b039d..000000000000 --- a/compiler/src/dotty/tools/dotc/repl/AmmoniteReader.scala +++ /dev/null @@ -1,82 +0,0 @@ -package dotty.tools -package dotc -package repl - -import core.Contexts._ -import ammonite.terminal._ -import LazyList._ -import Ansi.Color -import filters._ -import BasicFilters._ -import GUILikeFilters._ -import util.SourceFile -import printing.SyntaxHighlighting - -class AmmoniteReader(val interpreter: Interpreter)(implicit ctx: Context) extends InteractiveReader { - val interactive = true - - def incompleteInput(str: String): Boolean = - interpreter.delayOutputDuring(interpreter.interpret(str)) match { - case Interpreter.Incomplete => true - case _ => false - } - - val reader = new java.io.InputStreamReader(System.in) - val writer = new java.io.OutputStreamWriter(System.out) - val cutPasteFilter = ReadlineFilters.CutPasteFilter() - var history = List.empty[String] - val selectionFilter = GUILikeFilters.SelectionFilter(indent = 2) - val multilineFilter: Filter = Filter("multilineFilter") { - case TermState(lb ~: rest, b, c, _) - if (lb == 10 || lb == 13) && incompleteInput(b.mkString) => - BasicFilters.injectNewLine(b, c, rest, indent = 2) - } - - def readLine(prompt: String): String = { - val historyFilter = new HistoryFilter( - () => history.toVector, - Console.BLUE, - AnsiNav.resetForegroundColor - ) - - val allFilters = Filter.merge( - UndoFilter(), - historyFilter, - selectionFilter, - GUILikeFilters.altFilter, - GUILikeFilters.fnFilter, - ReadlineFilters.navFilter, - cutPasteFilter, - multilineFilter, - BasicFilters.all - ) - - Terminal.readLine( - Console.BLUE + prompt + Console.RESET, - reader, - writer, - allFilters, - displayTransform = (buffer, cursor) => { - val coloredBuffer = - if (ctx.useColors) SyntaxHighlighting(buffer) - else buffer - - val ansiBuffer = Ansi.Str.parse(coloredBuffer.toVector) - val (newBuffer, cursorOffset) = SelectionFilter.mangleBuffer( - selectionFilter, ansiBuffer, cursor, Ansi.Reversed.On - ) - val newNewBuffer = HistoryFilter.mangleBuffer( - historyFilter, newBuffer, cursor, - Ansi.Color.Green - ) - - (newNewBuffer, cursorOffset) - } - ) match { - case Some(res) => - history = res :: history; - res - case None => ":q" - } - } -} diff --git a/compiler/src/dotty/tools/dotc/repl/CompilingInterpreter.scala b/compiler/src/dotty/tools/dotc/repl/CompilingInterpreter.scala deleted file mode 100644 index 85f47692f4fa..000000000000 --- a/compiler/src/dotty/tools/dotc/repl/CompilingInterpreter.scala +++ /dev/null @@ -1,1024 +0,0 @@ -package dotty.tools -package dotc -package repl - -import java.io.{ - File, PrintWriter, PrintStream, StringWriter, Writer, OutputStream, - ByteArrayOutputStream => ByteOutputStream -} -import java.lang.{Class, ClassLoader, Thread, System, StringBuffer} -import java.net.{URL, URLClassLoader} - -import scala.collection.immutable.ListSet -import scala.collection.mutable -import scala.collection.mutable.{ListBuffer, HashSet, ArrayBuffer} - -//import ast.parser.SyntaxAnalyzer -import io.{PlainFile, VirtualDirectory} -import dotty.tools.io.{PlainDirectory, Directory} -import reporting.{ConsoleReporter, Reporter} -import core.Flags -import util.{SourceFile, NameTransformer} -import io.ClassPath -import ast.Trees._ -import parsing.Parsers._ -import core._ -import dotty.tools.backend.jvm.GenBCode -import Symbols._, Types._, Contexts._, StdNames._, Names._, NameOps._ -import Decorators._ -import scala.util.control.NonFatal -import printing.SyntaxHighlighting - -/** An interpreter for Scala code which is based on the `dotc` compiler. - * - * The overall approach is based on compiling the requested code and then - * using a Java classloader and Java reflection to run the code - * and access its results. - * - * In more detail, a single compiler instance is used - * to accumulate all successfully compiled or interpreted Scala code. To - * "interpret" a line of code, the compiler generates a fresh object that - * includes the line of code and which has public definition(s) to export - * all variables defined by that code. To extract the result of an - * interpreted line to show the user, a second "result object" is created - * which imports the variables exported by the above object and then - * exports a single definition named "result". To accommodate user expressions - * that read from variables or methods defined in previous statements, "import" - * statements are used. - * - * This interpreter shares the strengths and weaknesses of using the - * full compiler-to-Java. The main strength is that interpreted code - * behaves exactly as does compiled code, including running at full speed. - * The main weakness is that redefining classes and methods is not handled - * properly, because rebinding at the Java level is technically difficult. - * - * @author Moez A. Abdel-Gawad - * @author Lex Spoon - * @author Martin Odersky - * - * @param out The output to use for diagnostics - * @param ictx The context to use for initialization of the interpreter, - * needed to access the current classpath. - */ -class CompilingInterpreter( - out: PrintWriter, - ictx: Context, - parentClassLoader: Option[ClassLoader] -) extends Compiler with Interpreter { - import ast.untpd._ - import CompilingInterpreter._ - - ictx.base.initialize()(ictx) - - /** directory to save .class files to */ - val virtualDirectory = - if (ictx.settings.d.isDefault(ictx)) new VirtualDirectory("(memory)", None) - else new PlainDirectory(new Directory(new java.io.File(ictx.settings.d.value(ictx)))) // for now, to help debugging - - /** A GenBCode phase that uses `virtualDirectory` for its output */ - private class REPLGenBCode extends GenBCode { - override def outputDir(implicit ctx: Context) = virtualDirectory - } - - /** Phases of this compiler use `REPLGenBCode` instead of `GenBCode`. */ - override def phases = Phases.replace( - classOf[GenBCode], _ => new REPLGenBCode :: Nil, super.phases) - - /** whether to print out result lines */ - private var printResults: Boolean = true - private var delayOutput: Boolean = false - - val previousOutput = ListBuffer.empty[String] - - override def lastOutput() = { - val prev = previousOutput.toList - previousOutput.clear() - prev - } - - override def delayOutputDuring[T](operation: => T): T = { - val old = delayOutput - try { - delayOutput = true - operation - } finally { - delayOutput = old - } - } - - /** Temporarily be quiet */ - override def beQuietDuring[T](operation: => T): T = { - val wasPrinting = printResults - try { - printResults = false - operation - } finally { - printResults = wasPrinting - } - } - - private def newReporter = - new ConsoleReporter(Console.in, out) { - override def printMessage(msg: String) = - if (!delayOutput) { - out.print(/*clean*/(msg) + "\n") - // Suppress clean for now for compiler messages - // Otherwise we will completely delete all references to - // line$object$ module classes. The previous interpreter did not - // have the project because the module class was written without the final `$' - // and therefore escaped the purge. We can turn this back on once - // we drop the final `$' from module classes. - out.flush() - } else { - previousOutput += (/*clean*/(msg) + "\n") - } - } - - /** the previous requests this interpreter has processed */ - private val prevRequests = new ArrayBuffer[Request]() - - /** the compiler's classpath, as URL's */ - val compilerClasspath: Seq[URL] = ictx.platform.classPath(ictx).asURLs - - /* A single class loader is used for all commands interpreted by this Interpreter. - It would also be possible to create a new class loader for each command - to interpret. The advantages of the current approach are: - - - Expressions are only evaluated one time. This is especially - significant for I/O, e.g. "val x = Console.readLine" - - The main disadvantage is: - - - Objects, classes, and methods cannot be rebound. Instead, definitions - shadow the old ones, and old code objects refer to the old - definitions. - */ - /** class loader used to load compiled code */ - val classLoader: ClassLoader = { - lazy val parent = new URLClassLoader(compilerClasspath.toArray, - classOf[Interpreter].getClassLoader) - - new AbstractFileClassLoader(virtualDirectory, parentClassLoader.getOrElse(parent)) - } - - // Set the current Java "context" class loader to this interpreter's class loader - Thread.currentThread.setContextClassLoader(classLoader) - - /** Parse a line into a sequence of trees. Returns None if the input is incomplete. */ - private def parse(line: String)(implicit ctx: Context): Option[List[Tree]] = { - var justNeedsMore = false - val reporter = newReporter - reporter.withIncompleteHandler { _ => _ => justNeedsMore = true } { - // simple parse: just parse it, nothing else - def simpleParse(code: String)(implicit ctx: Context): List[Tree] = { - val source = new SourceFile("", code.toCharArray()) - val parser = new Parser(source) - val (selfDef, stats) = parser.templateStatSeq() - stats - } - val trees = simpleParse(line)(ctx.fresh.setReporter(reporter)) - if (reporter.hasErrors) { - Some(Nil) // the result did not parse, so stop - } else if (justNeedsMore) { - None - } else { - Some(trees) - } - } - } - - /** Compile a SourceFile. Returns the root context of the run that compiled the file. - */ - def compileSources(sources: List[SourceFile])(implicit ctx: Context): Context = { - val reporter = newReporter - val run = newRun(ctx.fresh.setReporter(reporter)) - run.compileSources(sources) - run.runContext - } - - /** Compile a string. Returns true if there are no - * compilation errors, or false otherwise. - */ - def compileString(code: String)(implicit ctx: Context): Boolean = { - val runCtx = compileSources(List(new SourceFile("