Esempio n. 1
0
/** Emulates the (Gnu)R command as precisely as possible. */
public class RCommand {

  // CheckStyle: stop system..print check

  public static void main(String[] args) {
    doMain(args, null, true, System.in, System.out);
    // never returns
    throw RInternalError.shouldNotReachHere();
  }

  public static int doMain(
      String[] args, String[] env, boolean initial, InputStream inStream, OutputStream outStream) {
    RCmdOptions options = RCmdOptions.parseArguments(RCmdOptions.Client.R, args, false);
    options.printHelpAndVersion();
    PolyglotEngine vm =
        createPolyglotEngineFromCommandLine(options, false, initial, inStream, outStream, env);
    return readEvalPrint(vm);
  }

  /** The standard R script escapes spaces to "~+~" in "-e" and "-f" commands. */
  private static String unescapeSpace(String input) {
    return input.replace("~+~", " ");
  }

  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();
  }

  private static final Source GET_ECHO =
      RSource.fromTextInternal("invisible(getOption('echo'))", RSource.Internal.GET_ECHO);
  private static final Source QUIT_EOF =
      RSource.fromTextInternal("quit(\"default\", 0L, TRUE)", RSource.Internal.QUIT_EOF);

  /**
   * 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;
  }

  private static boolean doEcho(PolyglotEngine vm) {
    PolyglotEngine.Value echoValue = vm.eval(GET_ECHO);
    Object echo = echoValue.get();
    if (echo instanceof TruffleObject) {
      RLogicalVector echoVec = echoValue.as(RLogicalVector.class);
      return RRuntime.fromLogical(echoVec.getDataAt(0));
    } else if (echo instanceof Byte) {
      return RRuntime.fromLogical((Byte) echo);
    } else {
      throw RInternalError.shouldNotReachHere();
    }
  }

  private static String getContinuePrompt() {
    return RRuntime.asString(
        RRuntime.asAbstractVector(RContext.getInstance().stateROptions.getValue("continue")));
  }
}
Esempio n. 2
0
  /**
   * 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;
  }