interpreterloop.scala

来自「JAVA 语言的函数式编程扩展」· SCALA 代码 · 共 328 行

SCALA
328
字号
/* NSC -- new Scala compiler * Copyright 2005-2008 LAMP/EPFL * @author Alexander Spoon */// $Id: InterpreterLoop.scala 13882 2008-02-04 15:25:47Z washburn $package scala.tools.nscimport java.io.{BufferedReader, File, FileReader, PrintWriter}import java.io.IOExceptionimport java.lang.{ClassLoader, System}import scala.tools.nsc.{InterpreterResults => IR}import scala.tools.nsc.interpreter._/** The  *  <a href="http://scala-lang.org/" target="_top">Scala</a> *  interactive shell.  It provides a read-eval-print loop around *  the Interpreter class. *  After instantiation, clients should call the <code>main()</code> method. * *  <p>If no in0 is specified, then input will come from the console, and *  the class will attempt to provide input editing feature such as *  input history. * *  @author Moez A. Abdel-Gawad *  @author  Lex Spoon *  @version 1.2 */class InterpreterLoop(in0: Option[BufferedReader], out: PrintWriter) {  def this(in0: BufferedReader, out: PrintWriter) =    this(Some(in0), out)  def this() = this(None, new PrintWriter(Console.out))  /** The input stream from which interpreter commands come */  var in: InteractiveReader = _  //set by main()  /** The context class loader at the time this object was created */  protected val originalClassLoader =    Thread.currentThread.getContextClassLoader    var settings: Settings = _ // set by main()  var interpreter: Interpreter = null // set by createInterpreter()  def isettings = interpreter.isettings  /** A reverse list of commands to replay if the user    * requests a :replay */  var replayCommandsRev: List[String] = Nil  /** A list of commands to replay if the user requests a :replay */  def replayCommands = replayCommandsRev.reverse  /** Record a command for replay should the user requset a :replay */  def addReplay(cmd: String) =    replayCommandsRev = cmd :: replayCommandsRev  /** Close the interpreter, if there is one, and set    * interpreter to <code>null</code>. */  def closeInterpreter() {    if (interpreter ne null) {      interpreter.close      interpreter = null      Thread.currentThread.setContextClassLoader(originalClassLoader)    }  }  /* As soon as the Eclipse plugin no longer needs it, delete uglinessxxx,   * parentClassLoader0, and the parentClassLoader method in Interpreter   */  var uglinessxxx: ClassLoader = _  def parentClassLoader0: ClassLoader = uglinessxxx  /** Create a new interpreter.  Close the old one, if there    * is one. */  def createInterpreter() {    //closeInterpreter()    interpreter = new Interpreter(settings, out) {      override protected def parentClassLoader = parentClassLoader0    }    interpreter.setContextClassLoader()  }  /** Bind the settings so that evaluated code can modiy them */  def bindSettings() {    interpreter.beQuietDuring {      interpreter.compileString(InterpreterSettings.sourceCodeForClass)	      interpreter.bind(        "settings",        "scala.tools.nsc.InterpreterSettings",         isettings)    }  }  /** print a friendly help message */  def printHelp {    printWelcome    out.println("Type :load followed by a filename to load a Scala file.")    out.println("Type :replay to reset execution and replay all previous commands.")    out.println("Type :quit to exit the interpreter.")  }    /** Print a welcome message */  def printWelcome() {    out.println("Welcome to Scala " + Properties.versionString + " (" +                 System.getProperty("java.vm.name") + ", Java " + System.getProperty("java.version") + ")." )    out.println("Type in expressions to have them evaluated.")    out.println("Type :help for more information.")    out.flush()  }    /** Prompt to print when awaiting input */  val prompt = Properties.shellPromptString  /** The main read-eval-print loop for the interpreter.  It calls   *  <code>command()</code> for each line of input, and stops when   *  <code>command()</code> returns <code>false</code>.   */  def repl() {    var first = true    while (true) {      out.flush()      val line =        if (first) {          /* For some reason, the first interpreted command always takes           * a second or two.  So, wait until the welcome message           * has been printed before calling bindSettings.  That way,           * the user can read the welcome message while this           * command executes.           */          val futLine = scala.concurrent.ops.future(in.readLine(prompt))          bindSettings()          first = false          futLine()      } else {        in.readLine(prompt)      }      if (line eq null)        return ()  // assumes null means EOF      val (keepGoing, finalLineMaybe) = command(line)      if (!keepGoing)        return              finalLineMaybe match {        case Some(finalLine) => addReplay(finalLine)        case None => ()      }    }  }  /** interpret all lines from a specified file */  def interpretAllFrom(filename: String) {    val fileIn = try {      new FileReader(filename)    } catch {      case _:IOException =>        out.println("Error opening file: " + filename)        return    }    val oldIn = in    val oldReplay = replayCommandsRev    try {      val inFile = new BufferedReader(fileIn)      in = new SimpleReader(inFile, out, false)      out.println("Loading " + filename + "...")      out.flush      repl    } finally {      in = oldIn      replayCommandsRev = oldReplay      fileIn.close    }  }  /** create a new interpreter and replay all commands so far */  def replay() {    closeInterpreter()    createInterpreter()    for (cmd <- replayCommands) {      out.println("Replaying: " + cmd)      out.flush()  // because maybe cmd will have its own output      command(cmd)      out.println    }  }  /** Run one command submitted by the user.  Three values are returned:    * (1) whether to keep running, (2) the line to record for replay,    * if any. */  def command(line: String): (Boolean, Option[String]) = {    def withFile(command: String)(action: String => Unit) {      val spaceIdx = command.indexOf(' ')      if (spaceIdx <= 0) {        out.println("That command requires a filename to be specified.")        return ()      }      val filename = command.substring(spaceIdx).trim      if (! new File(filename).exists) {        out.println("That file does not exist")        return ()      }      action(filename)    }    val helpRegexp    = ":h(e(l(p)?)?)?"    val quitRegexp    = ":q(u(i(t)?)?)?"    val loadRegexp    = ":l(o(a(d)?)?)?.*"    val replayRegexp  = ":r(e(p(l(a(y)?)?)?)?)?.*"    var shouldReplay: Option[String] = None    if (line.matches(helpRegexp))      printHelp    else if (line.matches(quitRegexp))      return (false, None)    else if (line.matches(loadRegexp)) {      withFile(line)(f => {        interpretAllFrom(f)        shouldReplay = Some(line)      })    }    else if (line matches replayRegexp)      replay    else if (line startsWith ":")      out.println("Unknown command.  Type :help for help.")    else       shouldReplay = interpretStartingWith(line)    (true, shouldReplay)  }    /** Interpret expressions starting with the first line.    * Read lines until a complete compilation unit is available    * or until a syntax error has been seen.  If a full unit is    * read, go ahead and interpret it.  Return the full string    * to be recorded for replay, if any.    */  def interpretStartingWith(code: String): Option[String] = {    interpreter.interpret(code) match {      case IR.Success => Some(code)      case IR.Error => None      case IR.Incomplete =>        if (in.interactive && code.endsWith("\n\n")) {          out.println("You typed two blank lines.  Starting a new command.")          None        } else {          val nextLine = in.readLine("     | ")          if (nextLine == null)            None  // end of file          else            interpretStartingWith(code + "\n" + nextLine)        }    }  }  def loadFiles(settings: Settings) {    settings match {      case settings: GenericRunnerSettings =>        for (filename <- settings.loadfiles.value) {          val cmd = ":load " + filename          command(cmd)          replayCommandsRev = cmd :: replayCommandsRev          out.println()        }      case _ =>    }  }  def main(settings: Settings) {    this.settings = settings    in =       in0 match {        case Some(in0) =>          new SimpleReader(in0, out, true)        case None =>          val emacsShell = System.getProperty("env.emacs", "") != ""          //println("emacsShell="+emacsShell) //debug          if (settings.Xnojline.value || emacsShell)            new SimpleReader()          else            InteractiveReader.createDefault()      }    uglinessxxx = classOf[InterpreterLoop].getClassLoader    createInterpreter()    loadFiles(settings)    try {      if (interpreter.reporter.hasErrors) {        return // it is broken on startup; go ahead and exit      }      printWelcome()      repl()    } finally {      closeInterpreter()    }  }    /** process command-line arguments and do as they request */  def main(args: Array[String]) {    def error1(msg: String) { out.println("scala: " + msg) }    val command = new InterpreterCommand(List.fromArray(args), error1)    if (!command.ok || command.settings.help.value || command.settings.Xhelp.value) {      // either the command line is wrong, or the user      // explicitly requested a help listing      if (command.settings.help.value) out.println(command.usageMsg)      if (command.settings.Xhelp.value) out.println(command.xusageMsg)      out.flush    }    else      main(command.settings)  }}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?