/** * The read-eval-print loop, which can take input from a console, command line expression or a * file. There are two ways the repl can terminate: * * <ol> * <li>A {@code quit} command is executed successfully, which case the system exits from the * {@link Quit} {@code .Internal} . * <li>EOF on the input. * </ol> * * In case 2, we must implicitly execute a {@code quit("default, 0L, TRUE} command before exiting. * So,in either case, we never return. */ static int readEvalPrint(PolyglotEngine vm) { int lastStatus = 0; ContextInfo contextInfo = ContextInfo.getContextInfo(vm); ConsoleHandler consoleHandler = contextInfo.getConsoleHandler(); try { // console.println("initialize time: " + (System.currentTimeMillis() - start)); REPL: for (; ; ) { boolean doEcho = doEcho(vm); consoleHandler.setPrompt(doEcho ? "> " : null); try { String input = consoleHandler.readLine(); if (input == null) { throw new EOFException(); } String trInput = input.trim(); if (trInput.equals("") || trInput.charAt(0) == '#') { // nothing to parse continue; } String continuePrompt = getContinuePrompt(); StringBuffer sb = new StringBuffer(input); Source source = RSource.fromTextInternal(sb.toString(), RSource.Internal.SHELL_INPUT); while (true) { lastStatus = 0; try { try { vm.eval(source); // checked exceptions are wrapped in RuntimeExceptions } catch (RuntimeException e) { if (e.getCause() instanceof com.oracle.truffle.api.vm.IncompleteSourceException) { throw e.getCause().getCause(); } throw e; } } catch (IncompleteSourceException e) { // read another line of input consoleHandler.setPrompt(doEcho ? continuePrompt : null); String additionalInput = consoleHandler.readLine(); if (additionalInput == null) { throw new EOFException(); } sb.append(additionalInput); source = RSource.fromTextInternal(sb.toString(), RSource.Internal.SHELL_INPUT); // The only continuation in the while loop continue; } catch (ParseException e) { e.report(consoleHandler); lastStatus = 1; } catch (RError e) { // drop through to continue REPL and remember last eval was an error lastStatus = 1; } catch (JumpToTopLevelException e) { // drop through to continue REPL } catch (DebugExitException e) { throw (RuntimeException) e.getCause(); } catch (ExitException e) { // usually from quit int status = e.getStatus(); if (contextInfo.getParent() == null) { vm.dispose(); Utils.systemExit(status); } else { return status; } } catch (Throwable e) { RInternalError.reportErrorAndConsoleLog(e, consoleHandler, 0); // We continue the repl even though the system may be broken lastStatus = 1; } continue REPL; } } catch (UserInterruptException e) { // interrupted by ctrl-c } } } catch (JumpToTopLevelException | EOFException ex) { // JumpToTopLevelException can happen if user profile invokes browser (unlikely but // possible) try { vm.eval(QUIT_EOF); } catch (JumpToTopLevelException e) { Utils.systemExit(0); } catch (ExitException e) { // normal quit, but with exit code based on lastStatus if (contextInfo.getParent() == null) { Utils.systemExit(lastStatus); } else { return lastStatus; } } catch (Throwable e) { throw RInternalError.shouldNotReachHere(e); } } finally { vm.dispose(); } return 0; }
static PolyglotEngine createPolyglotEngineFromCommandLine( RCmdOptions options, boolean embedded, boolean initial, InputStream inStream, OutputStream outStream, String[] env) { RStartParams rsp = new RStartParams(options, embedded); String fileArg = options.getString(FILE); if (fileArg != null) { if (options.getStringList(EXPR) != null) { Utils.rSuicide("cannot use -e with -f or --file"); } if (!rsp.getSlave()) { rsp.setSaveAction(SA_TYPE.NOSAVE); } if (fileArg.equals("-")) { // means stdin, but still implies NO_SAVE fileArg = null; } else { fileArg = unescapeSpace(fileArg); } // cf GNU R rsp.setInteractive(false); } /* * Outputting the welcome message here has the virtue that the VM initialization delay * occurs later. However, it does not work in embedded mode as console redirects have not * been installed at this point. So we do it later in REmbedded. */ if (!rsp.getQuiet() && !embedded) { System.out.println(RRuntime.WELCOME_MESSAGE); } /* * Whether the input is from stdin, a file (-f), or an expression on the command line (-e) * it goes through the console. N.B. -f and -e can't be used together and this is already * checked. */ ConsoleHandler consoleHandler; if (fileArg != null) { List<String> lines; String filePath; try { /* * If initial==false, ~ expansion will not have been done and the open will fail. * It's harmless to always do it. */ File file = new File(Utils.tildeExpand(fileArg)); lines = Files.readAllLines(file.toPath()); filePath = file.getCanonicalPath(); } catch (IOException e) { if (initial) { throw Utils.rSuicide(String.format(RError.Message.NO_SUCH_FILE.message, fileArg)); } else { throw RError.error(RError.NO_CALLER, RError.Message.NO_SUCH_FILE, fileArg); } } consoleHandler = new StringConsoleHandler(lines, outStream, filePath); } else if (options.getStringList(EXPR) != null) { List<String> exprs = options.getStringList(EXPR); for (int i = 0; i < exprs.size(); i++) { exprs.set(i, unescapeSpace(exprs.get(i))); } if (!rsp.getSlave()) { rsp.setSaveAction(SA_TYPE.NOSAVE); } // cf GNU R rsp.setInteractive(false); consoleHandler = new StringConsoleHandler(exprs, outStream, RSource.Internal.EXPRESSION_INPUT.string); } else { /* * GnuR behavior differs from the manual entry for {@code interactive} in that {@code * --interactive} never applies to {@code -e/-f}, only to console input that has been * redirected from a pipe/file etc. * * If we are in embedded mode, the creation of ConsoleReader and the ConsoleHandler * should be lazy, as these may not be necessary and can cause hangs if stdin has been * redirected. */ Console sysConsole = System.console(); // TODO fix for context sessions boolean isInteractive = options.getBoolean(INTERACTIVE) || sysConsole != null; if (!isInteractive && rsp.getSaveAction() != SA_TYPE.SAVE && rsp.getSaveAction() != SA_TYPE.NOSAVE) { String msg = "you must specify '--save', '--no-save' or '--vanilla'"; if (initial) { throw Utils.rSuicide(msg); } else { throw RError.error(RError.NO_CALLER, RError.Message.GENERIC, msg); } } if (embedded) { consoleHandler = new EmbeddedConsoleHandler(rsp); } else { boolean useReadLine = !rsp.getNoReadline(); if (useReadLine) { consoleHandler = new JLineConsoleHandler(rsp, inStream, outStream); } else { consoleHandler = new DefaultConsoleHandler(inStream, outStream); } } } return ContextInfo.create( rsp, env, ContextKind.SHARE_NOTHING, initial ? null : RContext.getInstance(), consoleHandler) .createVM(); }
@TruffleBoundary static void printNoDimList(RAbstractContainer s, PrintContext printCtx) throws IOException { final PrintParameters pp = printCtx.parameters(); final PrintWriter out = printCtx.output(); final StringBuilder tagbuf = printCtx.getOrCreateTagBuffer(); // save the original length so that we can restore the original value int taglen = tagbuf.length(); int ns = s.getLength(); RAbstractStringVector names; names = Utils.castTo(RRuntime.asAbstractVector(s.getNames(dummyAttrProfiles))); if (ns > 0) { int npr = (ns <= pp.getMax() + 1) ? ns : pp.getMax(); /* '...max +1' ==> will omit at least 2 ==> plural in msg below */ for (int i = 0; i < npr; i++) { if (i > 0) { out.println(); } String ss = names == null ? null : Utils.<String>getDataAt(names, i); if (ss != null && !ss.isEmpty()) { /* * Bug for L <- list(`a\\b` = 1, `a\\c` = 2) : const char *ss = * translateChar(STRING_ELT(names, i)); */ if (taglen + ss.length() > TAGBUFLEN) { if (taglen <= TAGBUFLEN) { tagbuf.append("$..."); } } else { /* * we need to distinguish character NA from "NA", which is a valid (if * non-syntactic) name */ if (ss == RRuntime.STRING_NA) { tagbuf.append("$<NA>"); } else if (RDeparse.isValidName(ss)) { tagbuf.append(String.format("$%s", ss)); } else { tagbuf.append(String.format("$`%s`", ss)); } } } else { if (taglen + indexWidth(i) > TAGBUFLEN) { if (taglen <= TAGBUFLEN) { tagbuf.append("$..."); } } else { tagbuf.append(String.format("[[%d]]", i + 1)); } } out.println(tagbuf); Object si = s.getDataAtAsObject(i); if (si instanceof RAttributable && ((RAttributable) si).isObject()) { RContext.getEngine().printResult(si); } else { ValuePrinters.INSTANCE.print(si, printCtx); ValuePrinters.printNewLine(printCtx); } tagbuf.setLength(taglen); // reset tag buffer to the original value } if (npr < ns) { out.printf("\n [ reached getOption(\"max.print\") -- omitted %d entries ]", ns - npr); } } else { /* ns = length(s) == 0 */ /* Formal classes are represented as empty lists */ String className = null; if (printCtx.printerNode().isObject(s) && printCtx.printerNode().isMethodDispatchOn()) { RAbstractStringVector klass = Utils.castTo(RRuntime.asAbstractVector(s.getAttr(RRuntime.CLASS_ATTR_KEY))); if (klass != null && klass.getLength() == 1) { String ss = klass.getDataAt(0); String str = snprintf(200, ".__C__%s", ss); Frame frame = com.oracle.truffle.r.runtime.Utils.getActualCurrentFrame(); if (ReadVariableNode.lookupAny(str, frame, false) != null) { className = ss; } } } if (className != null) { out.printf("An object of class \"%s\"", className); } else { if (names != null) { out.print("named "); } out.print("list()"); } } }