interpreter.scala
来自「JAVA 语言的函数式编程扩展」· SCALA 代码 · 共 960 行 · 第 1/3 页
SCALA
960 行
val rhpairs = for { req <- prevRequests.toList.reverse handler <- req.handlers } yield (req, handler) select(rhpairs, wanted).reverse } val code = new StringBuffer val trailingBraces = new StringBuffer val accessPath = new StringBuffer val impname = compiler.nme.INTERPRETER_IMPORT_WRAPPER val currentImps = mutable.Set.empty[Name] // add code for a new object to hold some imports def addWrapper() { code.append("object " + impname + "{\n") trailingBraces.append("}\n") accessPath.append("." + impname) currentImps.clear } addWrapper() // loop through previous requests, adding imports // for each one for ((req,handler) <- reqsToUse) { // If the user entered an import, then just use it // add an import wrapping level if the import might // conflict with some other import if(handler.importsWildcard || currentImps.exists(handler.importedNames.contains)) if(!currentImps.isEmpty) addWrapper() if (handler.member.isInstanceOf[Import]) code.append(handler.member.toString + ";\n") // give wildcard imports a import wrapper all to their own if(handler.importsWildcard) addWrapper() else currentImps ++= handler.importedNames // For other requests, import each bound variable. // import them explicitly instead of with _, so that // ambiguity errors will not be generated. Also, quote // the name of the variable, so that we don't need to // handle quoting keywords separately. for (imv <- handler.boundNames) { if (currentImps.contains(imv)) addWrapper() code.append("import ") code.append(req.objectName + req.accessPath + ".`" + imv + "`;\n") currentImps += imv } } addWrapper() // Add one extra wrapper, to prevent warnings // in the frequent case of redefining // the value bound in the last interpreter // request. (code.toString, trailingBraces.toString, accessPath.toString) } /** Parse a line into a sequence of trees. Returns None if the input * is incomplete. */ private def parse(line: String): Option[List[Tree]] = { var justNeedsMore = false reporter.withIncompleteHandler((pos,msg) => {justNeedsMore = true}) { // simple parse: just parse it, nothing else def simpleParse(code: String): List[Tree] = { reporter.reset val unit = new CompilationUnit( new BatchSourceFile("<console>", code.toCharArray())) val scanner = new compiler.syntaxAnalyzer.UnitParser(unit); val xxx = scanner.templateStatSeq; (xxx._2) } val (trees) = simpleParse(line) if (reporter.hasErrors) { Some(Nil) // the result did not parse, so stop } else if (justNeedsMore) { None } else { Some(trees) } } } /** Compile an nsc SourceFile. Returns true if there are * no compilation errors, or false othrewise. */ def compileSources(sources: List[SourceFile]): Boolean = { val cr = new compiler.Run reporter.reset cr.compileSources(sources) !reporter.hasErrors } /** Compile a string. Returns true if there are no * compilation errors, or false otherwise. */ def compileString(code: String): Boolean = compileSources(List(new BatchSourceFile("<script>", code.toCharArray))) /** Build a request from the user. <code>trees</code> is <code>line</code> * after being parsed. */ private def buildRequest(trees: List[Tree], line: String, lineName: String): Request = new Request(line, lineName) private def chooseHandler(member: Tree): Option[MemberHandler] = member match { case member: DefDef => Some(new DefHandler(member)) case member: ValDef => Some(new ValHandler(member)) case member@Assign(Ident(_), _) => Some(new AssignHandler(member)) case member: ModuleDef => Some(new ModuleHandler(member)) case member: ClassDef => Some(new ClassHandler(member)) case member: TypeDef => Some(new TypeAliasHandler(member)) case member: Import => Some(new ImportHandler(member)) case DocDef(_, documented) => chooseHandler(documented) case member => Some(new GenericHandler(member)) } /** <p> * Interpret one line of input. All feedback, including parse errors * and evaluation results, are printed via the supplied compiler's * reporter. Values defined are available for future interpreted * strings. * </p> * <p> * The return value is whether the line was interpreter successfully, * e.g. that there were no parse errors. * </p> * * @param line ... * @return ... */ def interpret(line: String): IR.Result = { if (prevRequests.isEmpty) new compiler.Run // initialize the compiler // parse val trees = parse(indentCode(line)) match { case None => return IR.Incomplete case (Some(Nil)) => return IR.Error // parse error or empty input case Some(trees) => trees } trees match { case List(_:Assign) => () case List(_:TermTree) | List(_:Ident) | List(_:Select) => // Treat a single bare expression specially. // This is necessary due to it being hard to modify // code at a textual level, and it being hard to // submit an AST to the compiler. return interpret("val "+newVarName()+" = \n"+line) case _ => () } val lineName = newLineName // figure out what kind of request val req = buildRequest(trees, line, lineName) if (req eq null) return IR.Error // a disallowed statement type if (!req.compile) return IR.Error // an error happened during compilation, e.g. a type error val (interpreterResultString, succeeded) = req.loadAndRun if (printResults || !succeeded) { // print the result out.print(clean(interpreterResultString)) } // book-keeping if (succeeded) prevRequests += req if (succeeded) IR.Success else IR.Error } /** A counter used for numbering objects created by <code>bind()</code>. */ private var binderNum = 0 /** Bind a specified name to a specified value. The name may * later be used by expressions passed to interpret. * * @param name the variable name to bind * @param boundType the type of the variable, as a string * @param value the object value to bind to it * @return an indication of whether the binding succeeded */ def bind(name: String, boundType: String, value: Any): IR.Result = { val binderName = "binder" + binderNum binderNum += 1 compileString( "object " + binderName + "{ var value: " + boundType + " = _; " + " def set(x: Any) = value=x.asInstanceOf[" + boundType + "]; }") val binderObject = Class.forName(binderName, true, classLoader) val setterMethod = (binderObject .getDeclaredMethods .toList .find(meth => meth.getName == "set") .get) var argsHolder: Array[Any] = null // this roundabout approach is to try and // make sure the value is boxed argsHolder = List(value).toArray setterMethod.invoke(null, argsHolder.asInstanceOf[Array[AnyRef]]) interpret("val " + name + " = " + binderName + ".value") } /** <p> * This instance is no longer needed, so release any resources * it is using. * </p> * <p> * Specifically, this deletes the temporary directory used for holding * class files for this instance. This cannot safely be done after * each command is executed because of Java's demand loading. * </p> * <p> * Also, this flushes the reporter's output. * </p> */ def close() { Interpreter.deleteRecursively(classfilePath) reporter.flush() } /** A traverser that finds all mentioned identifiers, i.e. things * that need to be imported. * It might return extra names. */ private class ImportVarsTraverser(definedVars: List[Name]) extends Traverser { val importVars = new HashSet[Name]() override def traverse(ast: Tree) { ast match { case Ident(name) => importVars += name case _ => super.traverse(ast) } } } /** Class to handle one member among all the members included * in a single interpreter request. */ private sealed abstract class MemberHandler(val member: Tree) { val usedNames: List[Name] = { val ivt = new ImportVarsTraverser(boundNames) ivt.traverseTrees(List(member)) ivt.importVars.toList } val boundNames: List[Name] = Nil def valAndVarNames: List[Name] = Nil def defNames: List[Name] = Nil val importsWildcard = false val importedNames: Seq[Name] = Nil val definesImplicit = member match { case tree:MemberDef => tree.mods.hasFlag(symtab.Flags.IMPLICIT) case _ => false } def extraCodeToEvaluate(req: Request, code: PrintWriter) { } def resultExtractionCode(req: Request, code: PrintWriter) { } } private class GenericHandler(member: Tree) extends MemberHandler(member) private class ValHandler(member: ValDef) extends MemberHandler(member) { override val boundNames = List(member.name) override def valAndVarNames = boundNames override def resultExtractionCode(req: Request, code: PrintWriter) { val vname = member.name if (member.mods.isPublic && !(isGeneratedVarName(vname) && req.typeOf(compiler.encode(vname)) == "Unit")) { val prettyName = NameTransformer.decode(vname) code.print(" + \"" + prettyName + ": " + string2code(req.typeOf(vname)) + " = \" + " + " (if(" + req.fullPath(vname) + ".asInstanceOf[AnyRef] != null) " + " ((if(" + req.fullPath(vname) + ".toString().contains('\\n')) " + " \"\\n\" else \"\") + " + req.fullPath(vname) + ".toString() + \"\\n\") else \"null\\n\") ") } } } private class DefHandler(defDef: DefDef) extends MemberHandler(defDef) { override val boundNames = List(defDef.name) override def defNames = boundNames override def resultExtractionCode(req: Request, code: PrintWriter) {
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?