package org.rascalmpl.repl;
import static org.rascalmpl.interpreter.utils.ReadEvalPrintDialogMessages.parseErrorMessage;
import static org.rascalmpl.interpreter.utils.ReadEvalPrintDialogMessages.staticErrorMessage;
import static org.rascalmpl.interpreter.utils.ReadEvalPrintDialogMessages.throwMessage;
import static org.rascalmpl.interpreter.utils.ReadEvalPrintDialogMessages.throwableMessage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.net.URISyntaxException;
import java.util.Collection;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.stream.Collectors;
import org.rascalmpl.interpreter.Configuration;
import org.rascalmpl.interpreter.Evaluator;
import org.rascalmpl.interpreter.StackTrace;
import org.rascalmpl.interpreter.control_exceptions.InterruptException;
import org.rascalmpl.interpreter.control_exceptions.QuitException;
import org.rascalmpl.interpreter.control_exceptions.Throw;
import org.rascalmpl.interpreter.result.IRascalResult;
import org.rascalmpl.interpreter.result.Result;
import org.rascalmpl.interpreter.staticErrors.StaticError;
import org.rascalmpl.interpreter.utils.Timing;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.ideservices.IDEServices;
import org.rascalmpl.library.util.PathConfig;
import org.rascalmpl.parser.gtd.exception.ParseError;
import org.rascalmpl.uri.URIUtil;
import org.rascalmpl.value.IValue;
import jline.Terminal;
public abstract class RascalInterpreterREPL extends BaseRascalREPL {
protected Evaluator eval;
private boolean measureCommandTime;
private final OutputStream originalOutput;
public RascalInterpreterREPL(InputStream stdin, OutputStream stdout, boolean prettyPrompt, boolean allowColors, File persistentHistory, Terminal terminal)
throws IOException, URISyntaxException {
super(null, stdin, stdout, prettyPrompt, allowColors, persistentHistory, terminal, null);
originalOutput = stdout;
}
public void setMeasureCommandTime(boolean measureCommandTime) {
this.measureCommandTime = measureCommandTime;
}
public boolean getMeasureCommandTime() {
return measureCommandTime;
}
@Override
protected void initialize(PathConfig pcfg, Writer stdout, Writer stderr, IDEServices ideServices) {
eval = constructEvaluator(stdout, stderr);
eval.setREPL(this);
}
protected abstract Evaluator constructEvaluator(Writer stdout, Writer stderr);
@Override
protected PrintWriter getErrorWriter() {
return eval.getStdErr();
}
@Override
protected PrintWriter getOutputWriter() {
return eval.getStdOut();
}
@Override
public void stop() {
eval.interrupt();
super.stop();
}
@Override
protected void cancelRunningCommandRequested() {
eval.interrupt();
}
@Override
protected void terminateRequested() {
eval.interrupt();
}
@Override
protected void stackTraceRequested() {
StackTrace trace = eval.getStackTrace();
Writer err = getErrorWriter();
try {
err.write("Current stack trace:\n");
err.write(trace.toLinkedString());
err.flush();
}
catch (IOException e) {
}
}
@Override
protected IRascalResult evalStatement(String statement, String lastLine) throws InterruptedException {
try {
Result<IValue> value;
long duration;
synchronized(eval) {
Timing tm = new Timing();
tm.start();
value = eval.eval(null, statement, URIUtil.rootLocation("prompt"));
duration = tm.duration();
}
if (measureCommandTime) {
eval.getStdErr().println("\nTime: " + duration + "ms");
}
return value;
}
catch (InterruptException ie) {
eval.getStdErr().println("Interrupted");
eval.getStdErr().println(ie.getRascalStackTrace().toLinkedString());
return null;
}
catch (ParseError pe) {
eval.getStdErr().println(parseErrorMessage(lastLine, "prompt", pe));
return null;
}
catch (StaticError e) {
eval.getStdErr().println(staticErrorMessage(e));
return null;
}
catch (Throw e) {
eval.getStdErr().println(throwMessage(e));
return null;
}
catch (QuitException q) {
eval.getStdErr().println("Quiting REPL");
throw new InterruptedException();
}
catch (Throwable e) {
eval.getStdErr().println(throwableMessage(e, eval.getStackTrace()));
return null;
}
}
@Override
protected boolean isStatementComplete(String command) {
try {
eval.parseCommand(null, command, URIUtil.rootLocation("prompt"));
}
catch (ParseError pe) {
String[] commandLines = command.split("\n");
int lastLine = commandLines.length;
int lastColumn = commandLines[lastLine - 1].length();
if (pe.getEndLine() + 1 == lastLine && lastColumn <= pe.getEndColumn()) {
return false;
}
}
return true;
}
@Override
protected Collection<String> completePartialIdentifier(String line, int cursor, String qualifier, String term) {
return eval.completePartialIdentifier(qualifier, term);
}
@Override
protected Collection<String> completeModule(String qualifier, String partialModuleName) {
List<String> entries = eval.getRascalResolver().listModuleEntries(qualifier);
if (entries != null && entries.size() > 0) {
if (entries.contains(partialModuleName)) {
// we have a full directory name (at least the option)
List<String> subEntries = eval.getRascalResolver().listModuleEntries(qualifier + "::" + partialModuleName);
if (subEntries != null) {
entries.remove(partialModuleName);
subEntries.forEach(e -> entries.add(partialModuleName + "::" + e));
}
}
return entries.stream()
.filter(m -> m.startsWith(partialModuleName))
.map(s -> qualifier.isEmpty() ? s : qualifier + "::" + s)
.sorted()
.collect(Collectors.toList());
}
return null;
}
private static final SortedSet<String> commandLineOptions = new TreeSet<>();
static {
commandLineOptions.add(Configuration.GENERATOR_PROFILING_PROPERTY.substring("rascal.".length()));
commandLineOptions.add(Configuration.PROFILING_PROPERTY.substring("rascal.".length()));
commandLineOptions.add(Configuration.ERRORS_PROPERTY.substring("rascal.".length()));
commandLineOptions.add(Configuration.TRACING_PROPERTY.substring("rascal.".length()));
}
@Override
protected SortedSet<String> getCommandLineOptions() {
return commandLineOptions;
}
public Terminal getTerminal() {
return reader.getTerminal();
}
public InputStream getInput() {
return reader.getInput();
}
public OutputStream getOutput() {
return originalOutput;
}
}