public static void main(String[] args) {
    final MessageReader<Request> reader = new MessageReader<>(new BufferedInputStream(System.in), Request.class);
    final MessageWriter<Response> writer = new MessageWriter<>(new BufferedOutputStream(System.out));

    try (JShell shell = JShell.create()) {
      configureJShell(args, shell);
      while (true) {
        final Request request = reader.receive(NULL_CONSUMER);
        if (request == null) {
          break;
        }
        final Request.Command command = request.getCommand();
        if (command == Request.Command.EXIT) {
          return;
        }

        final Response response = new Response(request.getUid());
        try {
          // first, handle eval classpath if any
          final List<String> cp = request.getClassPath();
          if (cp != null && !cp.isEmpty()) {
            for (String path : cp) {
              shell.addToClasspath(path);
            }
          }

          if (command == Request.Command.DROP_STATE) {
            shell.snippets().forEach(snippet -> exportEvents(shell, shell.drop(snippet), response));
          }
          else if (command == Request.Command.EVAL) {
            final List<String> toEval = new ArrayList<>();

            String input = request.getCodeText();
            do {
              final SourceCodeAnalysis.CompletionInfo info = shell.sourceCodeAnalysis().analyzeCompletion(input);
              final SourceCodeAnalysis.Completeness completeness = info.completeness();
              if (completeness.isComplete()) {
                toEval.add(completeness == SourceCodeAnalysis.Completeness.COMPLETE_WITH_SEMI ? info.source() + ";" : info.source());
              }
              else if (completeness != SourceCodeAnalysis.Completeness.EMPTY){
                // we try to evaluate even the snippets containing errors so that those errors will be displayed
                toEval.add(info.source());
              }
              input = info.remaining();
            }
            while (input != null && !input.isEmpty());

            for (String inputElement : toEval) {
              exportEvents(shell, shell.eval(inputElement), response);
            }
          }
        }
        finally {
          writer.send(response);
        }
      }
    }
    catch (Throwable e) {
      e.printStackTrace();
    }
    finally {
      System.out.println("\nJShell terminated.");
    }
  }
public static void main(String[] args)