All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.reactiverse.es4x.impl.REPLVerticle Maven / Gradle / Ivy

/*
 * Copyright 2018 Red Hat, Inc.
 *
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License v1.0
 *  and Apache License v2.0 which accompanies this distribution.
 *
 *  The Eclipse Public License is available at
 *  http://www.eclipse.org/legal/epl-v10.html
 *
 *  The Apache License v2.0 is available at
 *  http://www.opensource.org/licenses/apache2.0.php
 *
 *  You may elect to redistribute this code under either of these licenses.
 */
package io.reactiverse.es4x.impl;

import io.reactiverse.es4x.Runtime;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.impl.logging.Logger;
import io.vertx.core.impl.logging.LoggerFactory;
import io.vertx.core.parsetools.RecordParser;
import org.graalvm.polyglot.PolyglotException;
import org.graalvm.polyglot.Value;

import java.io.*;
import java.nio.charset.Charset;
import java.util.concurrent.atomic.AtomicBoolean;

public class REPLVerticle extends AbstractVerticle {

  private static final AtomicBoolean ACTIVE = new AtomicBoolean(false);

  private final Logger log = LoggerFactory.getLogger(REPLVerticle.class);

  private final Runtime runtime;
  private final StringBuilder buffer = new StringBuilder();

  public REPLVerticle(Runtime runtime) {
    this.runtime = runtime;
  }

  private synchronized String updateBuffer(String line, boolean resetOrPrepend) {
    if (resetOrPrepend) {
      buffer.append(line);
      final String statement = buffer.toString();
      // reset the buffer
      buffer.setLength(0);
      return statement;
    } else {
      // incomplete source, do not handle as error and
      // continue appending to the previous buffer
      buffer.insert(0, line);
      return null;
    }
  }

  private RecordParser stdin;
  private boolean cancel = false;

  @Override
  public void start() {
    if (ACTIVE.compareAndSet(false, true)) {
      stdin = RecordParser.newDelimited(System.getProperty("line.separator"), line -> {
        // we really want the system charset to not mess up with user input
        final String statement = updateBuffer(line.toString(Charset.defaultCharset()), true);

        // no-op
        if (statement == null || statement.length() == 0) {
          return;
        }

        try {
          Value parsed = runtime.parse(statement, true);
          System.out.println("\u001B[1;90m" + parsed.execute() + "\u001B[0m");
        } catch (PolyglotException t) {
          if (t.isIncompleteSource()) {
            updateBuffer(statement, false);
            return;
          }

          System.out.println("\u001B[1m\u001B[31m" + t.getMessage() + "\u001B[0m");

          if (t.isExit() || t.isResourceExhausted()) {
            // polyglot engine is requesting to exit
            // REPLVerticle is cancelled, close the loader
            cancel = true;
            ACTIVE.set(false);
            vertx.close(v -> {
              // force a error code out
              System.exit(1);
            });
          }
        } catch (Exception ex) {
          log.error(ex.getMessage(), ex);
        } finally {
          System.out.print("js:> ");
          System.out.flush();
        }
      });

      // setup is complete
      String script = System.getProperty("script");

      if (script != null) {
        // eval this script at start
        stdin.handle(Buffer.buffer(script + System.getProperty("line.separator")));
      } else {
        // delay the show of the prompt so it doesn't overlap with the startup logging
        vertx.setTimer(100, t -> {
          System.out.print("js:> ");
          System.out.flush();
        });
      }

      // start the repl cycle
      vertx.setPeriodic(100, l -> {
        if (cancel) {
          vertx.cancelTimer(l);
          return;
        }
        // read stdin
        try {
          int available = System.in.available();
          if (available > 0) {
            byte[] data = new byte[available];
            int bytes = System.in.read(data);
            // end of stream
            if (bytes == -1) {
              cancel = true;
              return;
            }
            // read incomplete
            if (bytes != available) {
              // not all data was read
              byte[] tmp = new byte[bytes];
              System.arraycopy(data, 0, tmp, 0, bytes);
              data = tmp;
            }
            stdin.handle(Buffer.buffer(data));
          }
        } catch (IOException e) {
          e.printStackTrace();
          cancel = true;
        }
      });
    }
  }

  @Override
  public void stop() {
    cancel = true;
    ACTIVE.set(false);
    System.out.println("\u001B[1mShell Terminated.\u001B[0m");
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy