1 package org.simantics.scl.compiler.commands;
3 import java.io.BufferedReader;
4 import java.io.FileNotFoundException;
5 import java.io.FileOutputStream;
6 import java.io.IOException;
7 import java.io.PrintStream;
9 import java.io.StringReader;
10 import java.io.UnsupportedEncodingException;
11 import java.util.ArrayList;
12 import java.util.Collection;
13 import java.util.Collections;
14 import java.util.List;
18 import org.simantics.scl.compiler.common.names.Names;
19 import org.simantics.scl.compiler.constants.StringConstant;
20 import org.simantics.scl.compiler.elaboration.expressions.EApply;
21 import org.simantics.scl.compiler.elaboration.expressions.EBlock;
22 import org.simantics.scl.compiler.elaboration.expressions.EConstant;
23 import org.simantics.scl.compiler.elaboration.expressions.EExternalConstant;
24 import org.simantics.scl.compiler.elaboration.expressions.ELiteral;
25 import org.simantics.scl.compiler.elaboration.expressions.EVariable;
26 import org.simantics.scl.compiler.elaboration.expressions.Expression;
27 import org.simantics.scl.compiler.elaboration.expressions.Variable;
28 import org.simantics.scl.compiler.elaboration.expressions.block.GuardStatement;
29 import org.simantics.scl.compiler.elaboration.expressions.block.Statement;
30 import org.simantics.scl.compiler.environment.AbstractLocalEnvironment;
31 import org.simantics.scl.compiler.environment.Environment;
32 import org.simantics.scl.compiler.environment.LocalEnvironment;
33 import org.simantics.scl.compiler.environment.specification.EnvironmentSpecification;
34 import org.simantics.scl.compiler.errors.CompilationError;
35 import org.simantics.scl.compiler.errors.Locations;
36 import org.simantics.scl.compiler.internal.codegen.utils.NameMangling;
37 import org.simantics.scl.compiler.internal.parsing.exceptions.SCLSyntaxErrorException;
38 import org.simantics.scl.compiler.internal.parsing.parser.SCLParserImpl;
39 import org.simantics.scl.compiler.internal.parsing.utils.LaxUTF8Reader;
40 import org.simantics.scl.compiler.internal.parsing.utils.MemoReader;
41 import org.simantics.scl.compiler.module.ImportDeclaration;
42 import org.simantics.scl.compiler.module.repository.ImportFailure;
43 import org.simantics.scl.compiler.module.repository.ImportFailureException;
44 import org.simantics.scl.compiler.module.repository.ModuleRepository;
45 import org.simantics.scl.compiler.module.repository.UpdateListener;
46 import org.simantics.scl.compiler.runtime.RuntimeEnvironment;
47 import org.simantics.scl.compiler.top.ExpressionEvaluator;
48 import org.simantics.scl.compiler.top.LocalStorage;
49 import org.simantics.scl.compiler.top.SCLExpressionCompilationException;
50 import org.simantics.scl.compiler.types.Type;
51 import org.simantics.scl.compiler.types.Types;
52 import org.simantics.scl.runtime.SCLContext;
53 import org.simantics.scl.runtime.function.Function;
54 import org.simantics.scl.runtime.function.FunctionImpl2;
55 import org.simantics.scl.runtime.reporting.DelegatingSCLReportingHandler;
56 import org.simantics.scl.runtime.reporting.SCLReporting;
57 import org.simantics.scl.runtime.reporting.SCLReportingHandler;
58 import org.simantics.scl.runtime.tuple.Tuple0;
60 import gnu.trove.map.hash.THashMap;
61 import gnu.trove.procedure.TObjectProcedure;
62 import gnu.trove.set.hash.THashSet;
65 public class CommandSession {
67 ModuleRepository moduleRepository;
68 SCLReportingHandler defaultHandler;
70 RuntimeEnvironment runtimeEnvironment;
71 ValueToStringConverter valueToStringConverter;
73 ArrayList<CommandSessionImportEntry> importEntries = new ArrayList<CommandSessionImportEntry>();
75 THashMap<String,Object> variableValues = new THashMap<String,Object>();
76 THashMap<String,Type> variableTypes = new THashMap<String,Type>();
78 PrintStream fileOutput;
79 private UpdateListener dependenciesListener;
82 * Only checks the commands for compilation errors but does not run them.
84 private boolean validateOnly;
86 public CommandSession(ModuleRepository moduleRepository, SCLReportingHandler handler) {
87 this.moduleRepository = moduleRepository;
88 this.defaultHandler = new PrintDecorator(
89 handler == null ? SCLReportingHandler.DEFAULT : handler);
90 updateRuntimeEnvironment(true);
93 private static EnvironmentSpecification createEnvironmentSpecification(Collection<CommandSessionImportEntry> importEntries) {
94 EnvironmentSpecification spec = new EnvironmentSpecification();
95 spec.importModule("Builtin", "");
96 spec.importModule("StandardLibrary", "");
97 spec.importModule("Expressions/Context", null);
98 for(CommandSessionImportEntry entry : importEntries)
99 if(!entry.disabled && !entry.hasError)
100 spec.importModule(entry.moduleName, entry.localName);
104 public void updateRuntimeEnvironment(boolean clearErrorsFlags) {
106 for(CommandSessionImportEntry entry : importEntries)
107 entry.hasError = false;
108 EnvironmentSpecification environmentSpecification = createEnvironmentSpecification(importEntries);
110 runtimeEnvironment = null;
112 if(dependenciesListener != null)
113 dependenciesListener.stopListening();
115 runtimeEnvironment = moduleRepository.createRuntimeEnvironment(
116 environmentSpecification,
117 getClass().getClassLoader(),
118 dependenciesListener);
119 } catch(ImportFailureException e) {
120 THashSet<String> failedModules = new THashSet<String>();
121 for(ImportFailure failure : e.failures) {
122 failedModules.add(failure.moduleName);
123 defaultHandler.printError(failure.toString());
124 if(failure.reason instanceof CompilationError[])
125 for(CompilationError error : (CompilationError[])failure.reason) {
126 defaultHandler.printError(" " + error.description);
129 for(CommandSessionImportEntry entry : importEntries)
130 if(failedModules.contains(entry.moduleName))
131 entry.hasError = true;
132 environmentSpecification = createEnvironmentSpecification(importEntries);
134 runtimeEnvironment = moduleRepository.createRuntimeEnvironment(
135 environmentSpecification,
136 getClass().getClassLoader()); // no listener here, because should listen also failed modules
137 } catch (ImportFailureException e1) {
138 for(ImportFailure failure : e1.failures)
139 defaultHandler.printError(failure.toString());
142 } catch(RuntimeException e) {
146 valueToStringConverter = new ValueToStringConverter(runtimeEnvironment);
149 public RuntimeEnvironment getRuntimeEnvironment() {
150 return runtimeEnvironment;
153 public ModuleRepository getModuleRepository() {
154 return moduleRepository;
157 private static class CancelExecution extends RuntimeException {
158 private static final long serialVersionUID = -6925642906311538873L;
161 private LocalStorage localStorage = new LocalStorage() {
163 public void store(String name, Object value, Type type) {
164 variableValues.put(name, value);
165 variableTypes.put(name, type);
169 private static class LocalFunction {
170 final Function function;
173 public LocalFunction(Function function, Type type) {
174 this.function = function;
179 private static final THashMap<String, LocalFunction> LOCAL_FUNCTIONS = new THashMap<String, LocalFunction>();
181 LOCAL_FUNCTIONS.put("runFromFile", new LocalFunction(new FunctionImpl2<CommandSession, String, Tuple0>() {
183 public Tuple0 apply(final CommandSession commandSession, String fileName) {
184 SCLContext context = SCLContext.getCurrent();
185 commandSession.runFromFile(fileName, (SCLReportingHandler)context.get(SCLReportingHandler.REPORTING_HANDLER));
186 return Tuple0.INSTANCE;
188 }, Types.functionE(Types.STRING, Types.PROC, Types.UNIT)));
189 LOCAL_FUNCTIONS.put("runTest", new LocalFunction(new FunctionImpl2<CommandSession, String, Tuple0>() {
191 public Tuple0 apply(final CommandSession commandSession, String fileName) {
192 SCLContext context = SCLContext.getCurrent();
193 SCLReportingHandler handler = (SCLReportingHandler)context.get(SCLReportingHandler.REPORTING_HANDLER);
195 BufferedReader reader = new BufferedReader(new LaxUTF8Reader(fileName));
197 new TestScriptExecutor(commandSession, reader, handler).execute();
201 } catch(IOException e) {
202 handler.printError(e.getMessage());
204 return Tuple0.INSTANCE;
206 }, Types.functionE(Types.STRING, Types.PROC, Types.UNIT)));
207 LOCAL_FUNCTIONS.put("reset", new LocalFunction(new FunctionImpl2<CommandSession, Tuple0, Tuple0>() {
209 public Tuple0 apply(CommandSession commandSession, Tuple0 dummy) {
210 commandSession.removeTransientImports();
211 commandSession.removeVariables();
212 commandSession.moduleRepository.getSourceRepository().checkUpdates();
213 commandSession.updateRuntimeEnvironment(true);
214 return Tuple0.INSTANCE;
216 }, Types.functionE(Types.UNIT, Types.PROC, Types.UNIT)));
217 LOCAL_FUNCTIONS.put("variables", new LocalFunction(new FunctionImpl2<CommandSession, Tuple0, List<String>>() {
219 public List<String> apply(CommandSession commandSession, Tuple0 dummy) {
220 ArrayList<String> result = new ArrayList<String>(commandSession.variableTypes.keySet());
221 Collections.sort(result);
224 }, Types.functionE(Types.PUNIT, Types.PROC, Types.list(Types.STRING))));
225 LOCAL_FUNCTIONS.put("startPrintingToFile", new LocalFunction(new FunctionImpl2<CommandSession, String, Tuple0>() {
227 public Tuple0 apply(final CommandSession commandSession, String fileName) {
229 if(commandSession.fileOutput != null) {
230 commandSession.fileOutput.close();
231 SCLReporting.printError("Printing to file was already enabled. Stopped the previous printing.");
233 commandSession.fileOutput = new PrintStream(fileName, "UTF-8");
234 } catch (FileNotFoundException e) {
235 throw new RuntimeException(e);
236 } catch (UnsupportedEncodingException e) {
237 throw new RuntimeException(e);
239 return Tuple0.INSTANCE;
241 }, Types.functionE(Types.STRING, Types.PROC, Types.UNIT)));
242 LOCAL_FUNCTIONS.put("startAppendingToFile", new LocalFunction(new FunctionImpl2<CommandSession, String, Tuple0>() {
244 public Tuple0 apply(final CommandSession commandSession, String fileName) {
246 if(commandSession.fileOutput != null) {
247 commandSession.fileOutput.close();
248 SCLReporting.printError("Printing to file was already enabled. Stopped the previous printing.");
250 FileOutputStream stream = new FileOutputStream(fileName, true);
251 commandSession.fileOutput = new PrintStream(stream, false, "UTF-8");
252 } catch (FileNotFoundException e) {
253 throw new RuntimeException(e);
254 } catch (UnsupportedEncodingException e) {
255 throw new RuntimeException(e);
257 return Tuple0.INSTANCE;
259 }, Types.functionE(Types.STRING, Types.PROC, Types.UNIT)));
260 LOCAL_FUNCTIONS.put("stopPrintingToFile", new LocalFunction(new FunctionImpl2<CommandSession, Tuple0, Tuple0>() {
262 public Tuple0 apply(final CommandSession commandSession, Tuple0 dummy) {
263 if(commandSession.fileOutput != null) {
264 commandSession.fileOutput.close();
265 commandSession.fileOutput = null;
267 return Tuple0.INSTANCE;
269 }, Types.functionE(Types.PUNIT, Types.PROC, Types.UNIT)));
272 private LocalEnvironment createLocalEnvironment() {
273 return new AbstractLocalEnvironment() {
274 Variable contextVariable = new Variable("context", Names.Expressions_Context_Context);
276 public Expression resolve(Environment environment, String localName) {
277 Type type = variableTypes.get(localName);
280 new EConstant(environment.getValue(Names.Expressions_Context_contextGet), type),
281 new EVariable(contextVariable),
282 new ELiteral(new StringConstant(localName))
284 LocalFunction localFunction = LOCAL_FUNCTIONS.get(localName);
285 if(localFunction != null) {
286 return new EExternalConstant(
287 localFunction.function.apply(CommandSession.this),
293 protected Variable[] getContextVariables() {
294 return new Variable[] { contextVariable };
297 public void forNames(TObjectProcedure<String> proc) {
298 for(String name : variableTypes.keySet())
300 for(String name : LOCAL_FUNCTIONS.keySet())
306 protected void removeTransientImports() {
307 ArrayList<CommandSessionImportEntry> newEntries = new ArrayList<CommandSessionImportEntry>(importEntries.size());
308 for(CommandSessionImportEntry entry : importEntries)
310 newEntries.add(entry);
311 importEntries = newEntries;
314 public THashMap<String,Type> localNamesForContentProposals() {
315 THashMap<String,Type> result = new THashMap<String,Type>();
316 for(Map.Entry<String,LocalFunction> entry : LOCAL_FUNCTIONS.entrySet())
317 result.put(entry.getKey(), entry.getValue().type);
318 result.putAll(variableTypes);
322 private CompiledCommand compile(Expression expression) throws SCLExpressionCompilationException {
323 LocalEnvironment localEnvironment = createLocalEnvironment();
324 if(runtimeEnvironment == null)
325 throw new SCLExpressionCompilationException(new CompilationError[] {
326 new CompilationError("Compilation failed: imports in the current environment have failed.")
328 ExpressionEvaluator evaluator = new ExpressionEvaluator(runtimeEnvironment, localStorage, expression);
329 Function command = (Function)evaluator
330 .localEnvironment(localEnvironment)
331 .decorateExpression(true)
332 .validateOnly(validateOnly)
334 return new CompiledCommand(command, evaluator.getType());
337 class PrintDecorator extends DelegatingSCLReportingHandler {
338 public PrintDecorator(SCLReportingHandler baseHandler) {
343 public void print(String text) {
345 if(fileOutput != null)
346 fileOutput.println(text);
350 public void printCommand(String command) {
351 super.printCommand(command);
352 if(fileOutput != null)
353 fileOutput.println("> " + command);
357 public void printError(String error) {
358 super.printError(error);
359 if(fileOutput != null)
360 fileOutput.println(error);
364 @SuppressWarnings("unchecked")
365 private void execute(MemoReader reader, Expression expression, final SCLReportingHandler handler) {
366 SCLContext context = SCLContext.getCurrent();
367 Object oldPrinter = context.put(SCLReportingHandler.REPORTING_HANDLER, handler);
369 CompiledCommand command;
371 handler.printCommand(reader.extractString(expression.location));
372 command = compile(expression);
373 } catch (SCLExpressionCompilationException e) {
376 CompilationError[] errors = e.getErrors();
377 for(CompilationError error : errors) {
378 if(error.location != Locations.NO_LOCATION)
379 handler.printError(reader.locationUnderlining(error.location));
380 handler.printError(error.description);
382 throw new CancelExecution();
384 reader.forgetEverythingBefore(Locations.endOf(expression.location));
387 Object resultValue = command.command.apply(variableValues);
388 String resultString = toString(resultValue, command.type);
389 if(!resultString.isEmpty())
390 handler.print(resultString);
392 } catch(Exception e) {
395 if(!(e instanceof CancelExecution)) {
396 if(e instanceof InterruptedException)
397 handler.printError("Execution interrupted.");
399 formatException(handler, e);
401 throw new CancelExecution();
403 context.put(SCLReportingHandler.REPORTING_HANDLER, oldPrinter);
407 private String toString(Object value, Type type) {
408 if(type.equals(Types.UNIT))
411 return valueToStringConverter.show(value, type);
412 } catch (SCLExpressionCompilationException e) {
413 return "<value of type " + type + ">";
417 class CommandParser extends SCLParserImpl {
418 SCLReportingHandler handler;
420 public CommandParser(SCLReportingHandler handler, MemoReader reader) {
422 this.reader = reader;
423 this.handler = handler;
428 if(currentBlock != null) {
430 currentBlock.location = Locations.combine(
431 currentBlock.getFirst().location,
432 currentBlock.getLast().location);
433 execute(reader, currentBlock, handler);
438 protected Object reduceStatementCommand() {
439 Statement statement = (Statement)get(0);
440 if(statement.mayBeRecursive()) {
441 if(currentBlock == null)
442 currentBlock = new EBlock();
443 currentBlock.addStatement(statement);
448 if(statement instanceof GuardStatement)
449 execute(reader, ((GuardStatement)statement).value, handler);
451 EBlock block = new EBlock();
452 block.addStatement(statement);
453 block.location = statement.location;
454 execute(reader, block, handler);
461 protected Object reduceImportCommand() {
465 ImportDeclaration importDeclaration = (ImportDeclaration)get(0);
466 handler.printCommand(reader.extractString(importDeclaration.location));
467 new CommandSessionImportEntry(importDeclaration.moduleName,
468 importDeclaration.localName).addTo(importEntries);
469 updateRuntimeEnvironment(false);
474 private void checkInterrupted() {
475 if(Thread.interrupted()) {
476 defaultHandler.printError("Execution interrupted.");
477 throw new CancelExecution();
481 private CompilationError[] validate(Reader commandReader) {
482 CommandParser parser = new CommandParser(defaultHandler, new MemoReader(commandReader));
485 parser.parseCommands();
486 parser.finishBlock();
487 return CompilationError.EMPTY_ARRAY;
488 } catch(SCLExpressionCompilationException e) {
489 return e.getErrors();
490 } catch(SCLSyntaxErrorException e) {
491 return new CompilationError[] { new CompilationError(e.location, e.getMessage()) };
492 } catch(Exception e) {
493 return new CompilationError[] { new CompilationError(Locations.NO_LOCATION, e.getMessage()) };
495 validateOnly = false;
499 public void execute(Reader commandReader, SCLReportingHandler handler) {
501 handler = defaultHandler;
502 else if (!(handler instanceof PrintDecorator))
503 handler = new PrintDecorator(handler);
504 CommandParser parser = new CommandParser(handler, new MemoReader(commandReader));
506 parser.parseCommands();
507 parser.finishBlock();
508 } catch(CancelExecution e) {
509 } catch(SCLSyntaxErrorException e) {
510 handler.printCommand(parser.reader.getLastCommand());
511 if(e.location != Locations.NO_LOCATION)
512 handler.printError(parser.reader.locationUnderlining(e.location));
513 handler.printError(e.getMessage());
514 } catch (Exception | AssertionError e) {
515 if(e instanceof InterruptedException)
516 handler.printError("Execution interrupted.");
518 formatException(handler, e);
522 public void execute(String command) {
523 execute(new StringReader(command), null);
526 public void execute(String command, SCLReportingHandler handler) {
527 execute(new StringReader(command), handler);
530 private static final String THIS_CLASS_NAME = CommandSession.class.getName();
532 public static void formatException(
533 SCLReportingHandler handler,
535 formatException(handler, null, e);
538 private static void formatException(
539 SCLReportingHandler handler,
540 StackTraceElement[] enclosingTrace,
542 StackTraceElement[] elements = e.getStackTrace();
543 Throwable cause = e.getCause();
545 formatException(handler, elements, cause);
546 handler.printError("Rethrown as ");
548 handler.printError(e.toString());
549 int endPos = elements.length;
550 if(enclosingTrace != null) {
551 int p = enclosingTrace.length;
552 while(endPos > 0 && p > 0 && elements[endPos-1].equals(enclosingTrace[p-1])) {
558 for(int i=0;i<endPos;++i) {
559 StackTraceElement element = elements[i];
560 if(element.getMethodName().equals("execute") &&
561 element.getClassName().equals(THIS_CLASS_NAME)) {
564 element = elements[endPos-1];
565 String className = element.getClassName();
566 if(className.startsWith("org.simantics.scl.compiler.top.SCLExpressionCompiler")
567 //|| element.getClassName().startsWith("org.simantics.scl.compiler.interpreted.")
568 || className.startsWith("org.simantics.scl.runtime.function.FunctionImpl")
569 //|| className.startsWith("tempsclpackage")
579 for(int i=0;i<endPos;++i) {
580 StringBuilder b = new StringBuilder();
581 StackTraceElement element = elements[i];
582 String className = element.getClassName();
583 if(className.equals("org.simantics.scl.compiler.interpreted.IApply")
584 || className.equals("org.simantics.scl.compiler.interpreted.ILet")
585 || className.startsWith("tempsclpackage"))
587 if(className.startsWith("org.simantics.scl.compiler.interpreted.ILambda")) {
588 b.append("\tat command line\n");
591 String methodName = element.getMethodName();
592 if(className.startsWith("org.simantics.scl.runtime.function.FunctionImpl") &&
593 methodName.equals("applyArray"))
595 String fileName = element.getFileName();
596 if("_SCL_Closure".equals(fileName))
599 if("_SCL_Module".equals(fileName)
600 || "_SCL_TypeClassInstance".equals(fileName))
603 .append(NameMangling.demangle(methodName))
604 .append('(').append(element.getLineNumber()).append(')');
607 handler.printError(b.toString());
611 public void setVariable(String name, Type type, Object value) {
612 variableValues.put(name, value);
613 variableTypes.put(name, type);
616 public Object getVariableValue(String name) {
617 return variableValues.get(name);
620 public Type getVariableType(String name) {
621 return variableTypes.get(name);
624 public void removeVariable(String name) {
625 variableValues.remove(name);
626 variableTypes.remove(name);
629 public void removeVariables() {
630 variableValues.clear();
631 variableTypes.clear();
634 public Set<String> getVariables() {
635 return variableTypes.keySet();
638 public ArrayList<CommandSessionImportEntry> getImportEntries() {
639 return importEntries;
642 public void setImportEntries(
643 ArrayList<CommandSessionImportEntry> importEntries) {
644 this.importEntries = importEntries;
645 updateRuntimeEnvironment(true);
648 public void runFromFile(String fileName, SCLReportingHandler handler) {
650 Reader reader = new LaxUTF8Reader(fileName);
652 execute(reader, handler);
656 } catch(IOException e) {
657 formatException(handler, e);
661 public static CompilationError[] validate(ModuleRepository moduleRepository,StringReader commandReader) {
662 CommandSession session = new CommandSession(moduleRepository, null);
663 return session.validate(commandReader);
666 public static CompilationError[] validate(ModuleRepository moduleRepository,String command) {
667 return validate(moduleRepository, new StringReader(command));
670 public CompilationError[] validate(String command) {
671 return validate(new StringReader(command));
674 public void setDependenciesListener(UpdateListener dependenciesListener) {
675 this.dependenciesListener = dependenciesListener;