package org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.repl;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.net.URISyntaxException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import org.rascalmpl.interpreter.utils.LimitedResultWriter.IOLimitReachedException;
import org.rascalmpl.library.Prelude;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.ExecutionTools;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.NameCompleter;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.NoSuchRascalFunction;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.RVMExecutable;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.RascalExecutionContext;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.RascalExecutionContextBuilder;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.Thrown;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.help.HelpManager;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.ideservices.IDEServices;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.java2rascal.Java2Rascal;
import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.repl.debug.DebugREPLFrameObserver;
import org.rascalmpl.library.lang.rascal.boot.IKernel;
import org.rascalmpl.library.lang.rascal.syntax.RascalParser;
import org.rascalmpl.library.util.PathConfig;
import org.rascalmpl.parser.Parser;
import org.rascalmpl.parser.gtd.exception.ParseError;
import org.rascalmpl.parser.gtd.result.action.IActionExecutor;
import org.rascalmpl.parser.gtd.result.out.DefaultNodeFlattener;
import org.rascalmpl.parser.uptr.UPTRNodeFactory;
import org.rascalmpl.parser.uptr.action.NoActionExecutor;
import org.rascalmpl.repl.CompletionResult;
import org.rascalmpl.repl.LimitedLineWriter;
import org.rascalmpl.repl.LimitedWriter;
import org.rascalmpl.uri.URIUtil;
import org.rascalmpl.value.IBool;
import org.rascalmpl.value.IConstructor;
import org.rascalmpl.value.IList;
import org.rascalmpl.value.IListWriter;
import org.rascalmpl.value.IMap;
import org.rascalmpl.value.IMapWriter;
import org.rascalmpl.value.ISet;
import org.rascalmpl.value.ISourceLocation;
import org.rascalmpl.value.IString;
import org.rascalmpl.value.ITuple;
import org.rascalmpl.value.IValue;
import org.rascalmpl.value.IValueFactory;
import org.rascalmpl.value.exceptions.FactTypeUseException;
import org.rascalmpl.value.io.StandardTextWriter;
import org.rascalmpl.value.type.Type;
import org.rascalmpl.value.type.TypeStore;
import org.rascalmpl.values.ValueFactoryFactory;
import org.rascalmpl.values.uptr.ITree;
import org.rascalmpl.values.uptr.RascalValueFactory;
import org.rascalmpl.values.uptr.TreeAdapter;
public class CommandExecutor {
private final static int LINE_LIMIT = 75;
private final static int CHAR_LIMIT = LINE_LIMIT * 20;
private PathConfig pcfg;
private PrintWriter stdout;
private PrintWriter stderr;
private final IValueFactory vf;
private String consoleInputName = "ConsoleInput";
public static String consoleInputPath = "/ConsoleInput.rsc";
private ISourceLocation consoleInputLocation;
private RVMExecutable rvmConsoleExecutable;
private SortedSet<String> vocabulary;
private final Prelude prelude;
boolean semiColonAdded = false;
private DebugREPLFrameObserver debugObserver;
private IDEServices ideServices;
protected final CompiledRascalREPL repl;
private HelpManager helpManager;
private HashMap<String, Variable> variables ;
private ArrayList<String> imports;
private HashMap<String,List<String>> syntaxDefinitions;
private HashMap<String,List<String>> functionDeclarations;
private HashMap<String,List<String>> dataDeclarations;
private Map<IValue, IValue> moduleVariables;
private final String shellModuleName = "CompiledRascalShell";
private boolean forceRecompilation = true;
private IKernel kernel;
private StandardTextWriter indentedPrettyPrinter;
private Settings settings;
private boolean kernel_coverage;
private boolean kernel_debug;
private boolean kernel_profile;
private boolean kernel_trace;
private boolean kernel_verbose;
private boolean compile_coverage;
private boolean compile_debug;
private boolean compile_enableAsserts;
private boolean compile_optimize;
private boolean compile_profile;
private boolean compile_testsuite;
private boolean compile_trace;
private boolean compile_verbose;
private boolean execute_coverage;
private boolean execute_debug;
private boolean execute_debugRVM;
private boolean execute_jvm;
private boolean execute_profile;
private boolean execute_testsuite;
private boolean execute_trace;
private boolean execute_verbose;
private boolean repl_verbose;
public CommandExecutor(PathConfig pcfg, PrintWriter stdout, PrintWriter stderr, IDEServices ideServices, CompiledRascalREPL repl) throws IOException, NoSuchRascalFunction, URISyntaxException {
vf = ValueFactoryFactory.getValueFactory();
prelude = new Prelude(vf);
settings = new Settings();
this.pcfg = settings.getPathConfig(pcfg).addSourceLoc(vf.sourceLocation("test-modules", "", ""));
this.stdout = stdout;
this.stderr = stderr;
this.ideServices = ideServices;
this.repl = repl;
consoleInputLocation = vf.sourceLocation("test-modules", "", consoleInputName + ".rsc");
try {
for(IValue isrc : pcfg.getSrcs()){
ideServices.watch(Paths.get(((ISourceLocation) isrc).getPath()));
}
} catch (IOException e){
System.err.println("Unable to watch file changes due to: " + e);
}
// options for the kernel
kernel_coverage = settings.getBool("kernel.coverage", false);
kernel_debug = settings.getBool("kernel.debug", false);
kernel_profile = settings.getBool("kernel.profile", false);
kernel_trace = settings.getBool("kernel.trace", false);
kernel_verbose = settings.getBool("kernel.verbose", false);
// options for compiler
compile_coverage = settings.getBool("compile.coverage", false);
compile_debug = settings.getBool("compile.debug", false);
compile_enableAsserts = settings.getBool("compile.enableAsserts", true);
compile_optimize = settings.getBool("compile.optimize", false);
compile_profile = settings.getBool("compile.profile", false);
compile_testsuite = settings.getBool("compile.testsuite", false);
compile_trace = settings.getBool("compile.trace", false);
compile_verbose = settings.getBool("compile.verbose", false);
// options per executed command
execute_coverage = settings.getBool("execute.coverage", false);
execute_debug = settings.getBool("execute.debug", false);
execute_debugRVM = settings.getBool("execute.debugRVM", false);
execute_jvm = settings.getBool("execute.jvm", true);
execute_profile = settings.getBool("execute.profile", false);
execute_testsuite = settings.getBool("execute.testsuite", false);
execute_trace = settings.getBool("execute.trace", false);
execute_verbose = settings.getBool("execute.verbose", false);
// options for the repl itself
repl_verbose = settings.getBool("repl.verbose", false);
IMapWriter w = vf.mapWriter();
//w.put(vf.string("bootstrapParser"), vf.string(""));
IMap CompiledRascalShellModuleTags = w.done();
// w = vf.mapWriter();
// w.put(vf.string(shellModuleName), CompiledRascalShellModuleTags);
// w.put(vf.string(consoleInputName), CompiledRascalShellModuleTags);
kernel = Java2Rascal.Builder.bridge(vf, pcfg, IKernel.class)
.coverage(kernel_coverage)
.debug(kernel_debug)
.profile(kernel_profile)
.trace(kernel_trace)
.verbose(kernel_verbose)
.build();
startupMessage(pcfg);
variables = new HashMap<>();
imports = new ArrayList<>();
syntaxDefinitions = new HashMap<>();
functionDeclarations = new HashMap<>();
dataDeclarations = new HashMap<>();
moduleVariables = new HashMap<>();
// helpManager is only initialized on first help or apropos call
indentedPrettyPrinter = new StandardTextWriter(true);
//singleLinePrettyPrinter = new StandardTextWriter(false);
stderr.println("Type 'help' for information or 'quit' to leave");
}
private void startupMessage(PathConfig pcfg){
if(repl_verbose){
System.err.println(pcfg);
}
}
public void reset(){
variables = new HashMap<>();
imports = new ArrayList<>();
syntaxDefinitions = new HashMap<>();
functionDeclarations = new HashMap<>();
dataDeclarations = new HashMap<>();
moduleVariables = new HashMap<>();
forceRecompilation = true;
vocabulary = null;
}
public void shutdown(){
kernel.shutdown();
}
public void setDebugObserver(DebugREPLFrameObserver observer){
this.debugObserver = observer;
if(kernel_debug){
kernel.setFrameObserver(observer);
}
}
private boolean noErrors(RVMExecutable exec){
return exec.getErrors().size() == 0;
}
private String capitalize(String s){
return s.substring(0,1).toUpperCase() + s.substring(1);
}
private boolean anyFilesChanged(){
for(Path p : ideServices.fileChanges()){
if(p.getFileName().toString().endsWith(".rsc")){
System.err.println("File changed: " + p);
return true;
}
}
return false;
}
private String getErrors(String modString, RVMExecutable exec){
ISet errors = exec.getErrors();
if(errors.size() == 0){
return "";
}
StringWriter sw = new StringWriter();
for(IValue m : errors){
IConstructor msg = (IConstructor) m;
String msgKind = msg.getName();
String txt = ((IString)msg.get("msg")).getValue();
ISourceLocation src = (ISourceLocation)msg.get("at");
String origin = " AT " + src.toString();
if(src.getPath().equals(consoleInputPath)){
int offset = src.getOffset();
String subarea = modString.substring(offset, offset + src.getLength());
origin = subarea.matches("[a-zA-Z0-9]+") ? "" : " IN '" + subarea + "'";
} else if(txt.contains("Cannot import")){
origin = "";
}
sw.append(capitalize(msgKind)).append(": ").append(txt).append(origin);
}
return sw.toString();
}
private void executeTests(String[] words){
IListWriter w = vf.listWriter();
if(words.length > 1){
for(int i = 1; i < words.length; i++){
String mname = words[i];
for(int j = 0; j < imports.size(); j++){ // Support abbreviations of already imported modules
if(imports.get(j).contains(mname)){
mname = imports.get(j);
break;
}
}
w.append(vf.string(mname));
}
} else {
if(imports.size() > 0){
for(int i = 0; i < imports.size(); i++){
w.append(vf.string(imports.get(i)));
}
} else {
stderr.println("No tests to execute; import modules with tests or give list of modules with tests");
return;
}
}
IValue res = kernel.rascalTests(w.done(), pcfg.asConstructor(kernel),
kernel.kw_rascalTests()
.verbose(execute_verbose)
.jvm(true)
.recompile(true)
);
stderr.println("executeTests: " + res);
}
public IConstructor executeTestsRaw(String mname){
return kernel.rascalTestsRaw(vf.list(vf.string(mname)), pcfg.asConstructor(kernel),
kernel.kw_rascalTests()
.verbose(execute_verbose)
.jvm(true)
.recompile(true)
);
}
private void moduleDeclarations(StringWriter w){
for(String imp : imports){
w.append("import ").append(imp).append(";\n");
}
for(String syn : syntaxDefinitions.keySet()){
for(String alt : syntaxDefinitions.get(syn)){
w.append(alt).append("\n");
}
}
for(String d : dataDeclarations.keySet()){
for(String alt : dataDeclarations.get(d)){
w.append(alt).append("\n");
}
}
for(String f : functionDeclarations.keySet()){
for(String alt : functionDeclarations.get(f)){
w.append(alt).append("\n");
}
}
for(String name : variables.keySet()){
Variable var = variables.get(name);
w.append(var.type).append(" ").append(name).append(";\n");
}
}
private IValue executeModule(String main, boolean onlyMainChanged) throws RascalShellExecutionException {
StringWriter w = new StringWriter();
w.append("module ConsoleInput\n");
moduleDeclarations(w);
w.append(main);
String modString = w.toString();
//System.err.println(modString);
try {
prelude.writeFile(consoleInputLocation, vf.list(vf.string(modString)));
IBool reuseConfig = vf.bool(onlyMainChanged && !forceRecompilation && !anyFilesChanged());
forceRecompilation = true;
IConstructor rvmProgram = kernel.compileAndMergeProgramIncremental(vf.string(consoleInputName),
reuseConfig,
pcfg.asConstructor(kernel),
kernel.kw_compileAndMergeProgramIncremental()
.optimize(compile_optimize)
.verbose(compile_verbose)
.jvm(true)
);
rvmConsoleExecutable = ExecutionTools.link(rvmProgram, ValueFactoryFactory.getValueFactory().bool(true), new TypeStore());
if(noErrors(rvmConsoleExecutable)){
RascalExecutionContext rex = RascalExecutionContextBuilder.normalContext(pcfg, stdout, stderr)
.forModule(shellModuleName)
.withModuleTags(rvmConsoleExecutable.getModuleTags())
.withModuleVariables(moduleVariables)
.debug(execute_debug)
.debugRVM(execute_debugRVM)
.testsuite(execute_testsuite)
.profile(execute_profile)
.trace(execute_trace)
.coverage(execute_coverage)
.jvm(true)
.verbose(execute_verbose)
.observedBy(debugObserver != null ? (kernel_debug ? debugObserver : debugObserver.getObserverWhenActiveBreakpoints()) : null)
.build();
IValue val = ExecutionTools.executeProgram(rvmConsoleExecutable, new HashMap<String,IValue>(), rex);
updateModuleVariables(rex.getModuleVariables());
forceRecompilation = false;
vocabulary = null;
return val;
} else {
System.err.println(getErrors(modString, rvmConsoleExecutable));
return null;
}
} catch (Thrown e){
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
if(e.getFrame() != null){
System.err.println(e);
debugObserver.exception(e.getFrame(), e);
} else {
System.err.println("Exception [debugger cannot break at null frame]: " + e);
}
return null; //throw new RascalShellExecutionException(sw.toString());
} catch (IOException e){
throw new RascalShellExecutionException("Error: " + (e.getMessage() != null ? e.getMessage() : e.toString()));
} catch (Exception e){
e.printStackTrace();
throw new RascalShellExecutionException("Error: " + (e.getMessage() != null ? e.getMessage() : e.toString()));
}
}
private boolean is(ITree tree, String consName){
return TreeAdapter.getConstructorName(tree).equals(consName);
}
private ITree get(ITree tree, String fieldName){
return TreeAdapter.getArg(tree, fieldName);
}
private boolean has(ITree tree, String fieldName){
try {
TreeAdapter.getArg(tree, fieldName);
return true;
} catch (Exception e){
return false;
}
}
// public String evalPrint(String statement, ISourceLocation rootLocation) throws IOException, ExecutionException{
// IValue result = eval(statement, rootLocation);
// return resultToString(result);
// }
public IValue eval(String statement, ISourceLocation rootLocation) throws RascalShellExecutionException, IOException {
ITree cmd = parseCommand(statement, rootLocation);
cmd = TreeAdapter.getStartTop(cmd);
if(is(cmd, "expression")){
return evalExpression(statement, get(cmd, "expression"));
}
if(is(cmd, "statement")){
return evalStatement(statement, get(cmd, "statement"));
}
if(is(cmd, "import")){
// try {
return evalImport(statement, get(cmd, "imported"));
// } catch (FactTypeUseException | IOException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// return null;
}
if(is(cmd, "declaration")){
//try {
return evalDeclaration(statement, get(cmd, "declaration"));
// } catch (FactTypeUseException | IOException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
}
return null;
}
private IValue evalExpression(String src, ITree exp) throws RascalShellExecutionException{
return executeModule("\nvalue main() = " + src + ";\n", true);
}
private void declareVar(String type, String name){
variables.put(name, new Variable(type, name));
}
private void updateVar(String name, IValue val){
moduleVariables.put(vf.string("ConsoleInput:" + name), val);
}
private void annotateVar(String name, String annoName, IValue annoVal){
IValue iname = vf.string("ConsoleInput:" + name);
IValue oldVal = moduleVariables.get(iname);
if(oldVal != null){
moduleVariables.put(iname, oldVal.asAnnotatable().setAnnotation(annoName, annoVal));
}
}
private boolean undeclareVar(String name){
return !(variables.remove(name) == null && moduleVariables.remove("ConsoleInput:" + name) == null);
}
private void updateModuleVariables(Map<IValue,IValue> newModuleVariables){
for(IValue ivar : moduleVariables.keySet()){
String name = ((IString) ivar).getValue();
IValue newVal = newModuleVariables.get("ConsoleInput:" + name);
if(newVal != null){
moduleVariables.put(ivar, newVal);
}
}
}
private IValue report(String msg) {
stdout.println(msg);
stdout.flush();
return null;
}
private IValue reportError(String msg) throws RascalShellExecutionException {
throw new RascalShellExecutionException("Error: " + msg);
}
private String getBaseVar(ITree assignable) throws FactTypeUseException, IOException{
if(is(assignable, "variable")){
return unparse(get(assignable, "qualifiedName"));
}
if(has(assignable, "receiver")){
return getBaseVar(get(assignable, "receiver"));
}
return null;
}
private String makeMain(String stat){
return "\nvalue main() { try { return " + stat + "} catch e: return e;}\n";
}
private String makeMainOk(){
return "\nvalue main() = \"ok\";\n";
}
private IValue evalStatement(String src, ITree stat) throws IOException, RascalShellExecutionException {
String consName = TreeAdapter.getConstructorName(stat);
switch(consName){
case "expression":
String innerExp = unparse(get(stat, "expression"));
return executeModule(makeMain(innerExp + ";"), true);
case "assignment":
//assignment: Assignable assignable Assignment operator Statement!functionDeclaration!variableDeclaration statement
ITree assignable = get(stat, "assignable");
String assignableName = TreeAdapter.getConstructorName(assignable);
IValue val;
switch(assignableName){
case "variable": case "subscript": case "slice": case "sliceStep": case "fieldAccess": {
String name = getBaseVar(assignable);
Variable var = variables.get(name);
if(var != null){
val = executeModule("\nvalue main() { " + src + "}\n", true);
updateVar(name, val);
return val;
} else {
val = executeModule(makeMain(unparse(get(stat, "statement"))), true);
declareVar(val.getType().toString(), name);
updateVar(name, val);
forceRecompilation = true;
return val;
}
}
case "annotation": {
String annoName = unparse(get(assignable, "annotation"));
String name = getBaseVar(assignable);
Variable var = variables.get(name);
val = executeModule(makeMain(unparse(get(stat, "statement"))), true);
if(var != null){
annotateVar(name, annoName, val);
}
return val;
}
case "ifDefinedOrDefault": {
String name = getBaseVar(assignable);
Variable var = variables.get(name);
val = executeModule(makeMain(src), true);
if(var == null){
declareVar(val.getType().toString(), name);
updateVar(name, val);
}
return val;
}
case "tuple": {
ITree elements = get(assignable, "elements");
val = executeModule(makeMain(unparse(get(stat, "statement"))), true);
ITuple tupleVal = (ITuple) val;
IList elemList = TreeAdapter.getListASTArgs(elements);
for(int i = 0; i < elemList.length(); i++){
ITree element = (ITree) elemList.get(i);
String elemName = getBaseVar((ITree)element);
if(elemName != null){
declareVar(val.getType().getFieldType(i).toString(), elemName);
updateVar(elemName, tupleVal.get(i));
forceRecompilation = true;
} else {
return reportError("Assignable is not supported: " + unparse((ITree)element));
}
}
return val;
}
case "constructor": {
consName = unparse(get(assignable, "name"));
ITree elements = get(assignable, "arguments");
val = executeModule(makeMain(unparse(get(stat, "statement"))), true);
if(val != null){
IConstructor consVal = (IConstructor) val;
if(consVal.getName().equals(consName)){
IList elemList = TreeAdapter.getListASTArgs(elements);
for(int i = 0; i < elemList.length(); i++){
ITree element = (ITree) elemList.get(i);
String elemName = getBaseVar((ITree)element);
if(elemName != null){
declareVar(consVal.getConstructorType().getFieldType(i).toString(), elemName);
updateVar(elemName, consVal.get(i));
} else {
return reportError("Assignable is not supported: " + unparse((ITree)element));
}
}
} else {
return reportError("Name mismatch in assignment: " + consName + " vs " + consVal.getName());
}
}
forceRecompilation = true;
return val;
}
default:
return reportError("Assignable is not supported: " + src);
}
default:
return executeModule(makeMain(unparse(stat)), true);
}
}
private IValue evalDeclaration(String src, ITree cmd) throws FactTypeUseException, IOException, RascalShellExecutionException{
IValue result = null;
if(is(cmd, "variable")){
ITree type = get(cmd, "type");
ITree variables = get(cmd, "variables");
IList vars = TreeAdapter.getListASTArgs(variables);
for(IValue ivar : vars){
ITree var = (ITree) ivar;
String name = unparse(get(var, "name"));
if(is(var, "initialized")){
String initial = unparse(get(var, "initial"));
declareVar(unparse(type), name);
try {
forceRecompilation = true;
result = executeModule(makeMain(name + " = " + initial + ";"), false);
updateVar(name, result);
} catch (RascalShellExecutionException e){
undeclareVar(name);
return null;
}
} else {
declareVar(unparse(type), name);
try {
forceRecompilation = true;
result = executeModule(makeMain("true;"), false);
} catch (RascalShellExecutionException e){
undeclareVar(name);
}
return null;
}
}
return result;
} else if(is(cmd, "dataAbstract") || is(cmd, "data")){
ITree userType = get(cmd, "user");
String name = unparse(get(userType, "name"));
List<String> alts = dataDeclarations.containsKey(name)
? dataDeclarations.get(name)
: new ArrayList<String>();
alts.add(src);
dataDeclarations.put(name, alts);
try {
result = executeModule(makeMain("true;"), false);
return null;
} catch (RascalShellExecutionException e) {
alts.remove(src);
dataDeclarations.put(name, alts);
throw e;
}
} else if(is(cmd, "function")){
ITree functionDeclaration = get(cmd, "functionDeclaration");
ITree signature = get(functionDeclaration, "signature");
String name = unparse(get(signature, "name"));
List<String> alts = functionDeclarations.containsKey(name)
? functionDeclarations.get(name)
: new ArrayList<String>();
alts.add(src);
functionDeclarations.put(name, alts);
try {
result = executeModule(makeMain("true;"), false);
return null;
} catch (RascalShellExecutionException e) {
alts.remove(src);
functionDeclarations.put(name, alts);
throw e;
}
}
return reportError("Not supported construct: " + src);
}
private IValue evalImport(String src, ITree imp) throws FactTypeUseException, IOException, RascalShellExecutionException{
IValue result;
if(is(imp, "default")){
String impName = unparse(get(get(imp, "module"), "name"));
if(!imports.contains(impName)){
imports.add(impName);
}
try {
forceRecompilation = true;
result = executeModule(makeMainOk(), false);
return null;
} catch (RascalShellExecutionException e){
imports.remove(impName);
throw e;
}
}
if(is(imp, "syntax")){
StringWriter w = new StringWriter();
TreeAdapter.unparse(get(get(imp, "syntax"), "defined"), w);
String name = w.toString();
List<String> alts = syntaxDefinitions.containsKey(name)
? syntaxDefinitions.get(name)
: new ArrayList<String>();
alts.add(src);
syntaxDefinitions.put(name, alts);
try {
result = executeModule(makeMainOk(), false);
return null;
} catch (RascalShellExecutionException e){
alts.remove(src);
syntaxDefinitions.put(name, alts);
throw e;
}
}
return null;
}
private boolean getBooleanValue(String val) throws RascalShellExecutionException{
switch(val){
case "true": return true;
case "false": return false;
default:
throw new RascalShellExecutionException("Error: '" + val + "' is not a boolean value");
}
}
private String unparse(ITree tree) throws FactTypeUseException, IOException{
StringWriter w = new StringWriter();
TreeAdapter.unparse(tree, w);
return w.toString();
}
//TODO merge with BaseRascalRepl
public String resultToString(IValue value) throws IOException {
StringWriter out = new StringWriter();
if (value == null) {
out.append("ok\n");
return out.toString();
}
Type type = value.getType();
if (type.isAbstractData() && type.isStrictSubtypeOf(RascalValueFactory.Tree)) {
out.append(type.toString());
out.append(": ");
// we unparse the tree
out.append("(" + type.toString() +") `");
TreeAdapter.yield((IConstructor)value, false, out);
out.append("`");
} else if(type.isAbstractData() && type.getName().equals("RuntimeException")){
out.append("Error: ");
out.append(value.toString());
} else {
out.append(type.toString());
out.append(": ");
try (Writer wrt = new LimitedWriter(new LimitedLineWriter(out, LINE_LIMIT), CHAR_LIMIT)) {
indentedPrettyPrinter.write(value, wrt);
}
catch (IOLimitReachedException e) {
// ignore since this is what we wanted
}
}
out.append("\n");
return out.toString();
}
private IValue showOptions(){
StringBuilder sb = new StringBuilder();
return report(
sb.append("profile: ").append(execute_profile).append("\n")
.append("trace: ").append(execute_trace).append("\n")
.append("coverage: ").append(execute_coverage).append("\n")
.toString()
);
}
public IValue evalShellCommand(String[] words) throws RascalShellExecutionException {
switch(words[0]){
case "set":
if(words.length == 1){
return showOptions();
}
if(words.length != 3){
return reportError("set requires two arguments");
}
String name = words[1];
String val = words[2];
switch(name){
case "profile":
execute_profile = getBooleanValue(val);
if(execute_profile && (execute_trace || execute_coverage)){
execute_trace = execute_coverage = false;
return showOptions();
}
return report(name + " set to " + execute_profile);
case "trace":
execute_trace = getBooleanValue(val);
if(execute_trace && (execute_profile || execute_coverage)){
execute_profile = execute_coverage = false;
return showOptions();
}
return report(name + " set to " + execute_trace);
case "coverage":
execute_coverage = getBooleanValue(val);
if(execute_coverage && (execute_profile || execute_trace)){
execute_profile = execute_trace = false;
return showOptions();
}
return report(name + " set to " + execute_coverage);
default:
return reportError("Unrecognized option: " + name);
}
case "help": case "apropos":
if(helpManager == null){
helpManager = new HelpManager(pcfg, stdout, stderr, ideServices);
}
helpManager.handleHelp(words);
break;
case "edit":
System.err.println("edit: " + words[1]);
ISourceLocation loc = pcfg.resolveModule(words[1]);
System.err.println("loc: " + loc);
ideServices.edit( Paths.get(loc.getPath()));
break;
case "declarations":
if(syntaxDefinitions.isEmpty()
&& dataDeclarations.isEmpty()
&& functionDeclarations.isEmpty()
&& variables.isEmpty()
&& imports.isEmpty()){
stderr.println("No declarations");
} else {
StringWriter w = new StringWriter();
moduleDeclarations(w);
stderr.println(w.toString());
}
break;
case "unimport":
if(words.length > 1){
for(int i = 1; i <words.length; i++){
if(imports.remove(words[i])){
stderr.println("Import " + words[i] + "removed");
} else {
stderr.println("No import " + words[i] + " found");
}
}
} else {
imports = new ArrayList<String>();
stderr.println("All imports removed");
}
executeModule(makeMain("true;"), false);
break;
case "undeclare":
if(words.length > 1){
for(int i = 1; i <words.length; i++){
if(!undeclareVar(words[i]) ||
syntaxDefinitions.remove(words[i]) == null ||
functionDeclarations.remove(words[i]) == null ||
dataDeclarations.remove(words[i]) == null){
stderr.println("No declaration for " + words[i] + " found");
} else {
stderr.println("Declaration for " + words[i] + " removed");
}
}
} else {
variables = new HashMap<>();
syntaxDefinitions = new HashMap<>();
functionDeclarations = new HashMap<>();
dataDeclarations = new HashMap<>();
stderr.println("All declarations removed");
}
executeModule(makeMain("true;"), false);
break;
case "clean":
cleanProject(words);
case "break":
debugObserver.getBreakPointManager().breakDirective(words);
break;
case "clear":
debugObserver.getBreakPointManager().clearDirective(words);
break;
case "ignore":
debugObserver.getBreakPointManager().ignoreDirective(words);
break;
case "enable":
debugObserver.getBreakPointManager().enableDirective(words);
break;
case "disable":
debugObserver.getBreakPointManager().disableDirective(words);
break;
case "test":
executeTests(words);
break;
}
stdout.flush();
return null;
}
private boolean endsNonEmpty(ITree stat, String consName, String partName){
return is(stat, consName) && !is(get(stat, partName), "emptyStatement");
}
private boolean mayComplete(ITree tree){
ITree cmd = TreeAdapter.getStartTop(tree);
if(is(cmd, "statement")){
ITree stat = get(cmd, "statement");
return endsNonEmpty(stat, "ifThen", "thenStatement")
|| endsNonEmpty(stat, "ifThenElse", "elseStatement")
|| endsNonEmpty(stat, "while", "body")
|| endsNonEmpty(stat, "solve", "body")
|| endsNonEmpty(stat, "assignment", "statement")
|| endsNonEmpty(stat, "for", "body")
|| endsNonEmpty(stat, "tryFinally", "finallyBody");
} else if(is(cmd, "import")){
return true;
}
return false;
}
public boolean isStatementComplete(String command) {
String[] words = command.split(" ");
if(words.length > 0 && CompiledRascalREPL.SHELL_VERBS.contains(words[0])){
return true;
}
ITree parseResult = null;
try {
parseResult = parseCommand(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()) {
semiColonAdded = false;
return false;
}
if (lastLine == 1 && !semiColonAdded && pe.getEndLine() + 1 == lastLine && lastColumn == pe.getEndColumn()) {
semiColonAdded = true;
boolean isComplete = isStatementComplete(command + ";");
semiColonAdded &= isComplete;
return isComplete;
}
return false;
}
return !semiColonAdded || mayComplete(parseResult);
}
public ITree parseCommand(String command, ISourceLocation location) {
//__setInterrupt(false);
IActionExecutor<ITree> actionExecutor = new NoActionExecutor();
ITree tree = new RascalParser().parse(Parser.START_COMMAND, location.getURI(), command.toCharArray(), actionExecutor, new DefaultNodeFlattener<IConstructor, ITree, ISourceLocation>(), new UPTRNodeFactory(true));
// if (!noBacktickOutsideStringConstant(command)) {
// tree = org.rascalmpl.semantics.dynamic.Import.parseFragments(this, tree, location, getCurrentModuleEnvironment());
// }
return tree;
}
public PrintWriter getStdErr() {
return stderr;
}
public PrintWriter getStdOut() {
return stdout;
}
SortedSet<String> getVocabulary(){
if(vocabulary != null){
return vocabulary;
}
ISet names = kernel.getIncrementalVocabulary();
vocabulary = new TreeSet<>();
for(IValue iname : names){
vocabulary.add(((IString) iname).getValue());
}
return vocabulary;
}
public Collection<String> completePartialIdentifier(String qualifier, String term) {
getVocabulary();
return completePartialIdentifier(new NameCompleter(), term).getResult();
}
private NameCompleter completePartialIdentifier(NameCompleter completer, String partialIdentifier) {
if (partialIdentifier == null || partialIdentifier.isEmpty()) {
throw new IllegalArgumentException("The behavior with empty string is undefined.");
}
if (partialIdentifier.startsWith("\\")) {
partialIdentifier = partialIdentifier.substring(1);
}
for(String completeName : vocabulary.tailSet(partialIdentifier)){
completer.add(completeName, partialIdentifier);
}
return completer;
}
public Collection<String> completeDeclaredIdentifier(String term) {
TreeSet<String> result = new TreeSet<String>();
for(String var : variables.keySet()){
if(var.startsWith(term)){
result.add(var);
}
}
for(String var : syntaxDefinitions.keySet()){
if(var.startsWith(term)){
result.add(var);
}
}
for(String var : functionDeclarations.keySet()){
if(var.startsWith(term)){
result.add(var);
}
}
for(String var : dataDeclarations.keySet()){
if(var.startsWith(term)){
result.add(var);
}
}
return result;
}
public Collection<String> completeImportedIdentifier(String term) {
TreeSet<String> result = null;
for(String mod : imports){
if(mod.startsWith(term)){
if(result == null){
result = new TreeSet<String>();
}
result.add(mod);
}
}
return result;
}
public CompletionResult completeModule(String line, int cursor){
return repl.completeModule(line, cursor);
}
private void cleanProject(String[] words) {
try {
Path binRoot = Paths.get(pcfg.getBin().getPath());
Files.walkFileTree(binRoot, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path filePath, BasicFileAttributes attrs) throws IOException {
System.err.println("delete: " + filePath);
Files.delete(filePath);
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e){
System.err.println("Could not clean project: " + e);
}
}
}
class Variable {
String name;
String type;
Variable(String type, String name){
this.name = name;
this.type = type;
}
}