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.ErrorSeverity;
36 import org.simantics.scl.compiler.errors.Locations;
37 import org.simantics.scl.compiler.internal.codegen.utils.NameMangling;
38 import org.simantics.scl.compiler.internal.parsing.exceptions.SCLSyntaxErrorException;
39 import org.simantics.scl.compiler.internal.parsing.parser.SCLParserImpl;
40 import org.simantics.scl.compiler.internal.parsing.utils.LaxUTF8Reader;
41 import org.simantics.scl.compiler.internal.parsing.utils.MemoReader;
42 import org.simantics.scl.compiler.module.ImportDeclaration;
43 import org.simantics.scl.compiler.module.repository.ImportFailure;
44 import org.simantics.scl.compiler.module.repository.ImportFailureException;
45 import org.simantics.scl.compiler.module.repository.ModuleRepository;
46 import org.simantics.scl.compiler.module.repository.UpdateListener;
47 import org.simantics.scl.compiler.runtime.RuntimeEnvironment;
48 import org.simantics.scl.compiler.top.ExpressionEvaluator;
49 import org.simantics.scl.compiler.top.LocalStorage;
50 import org.simantics.scl.compiler.top.SCLExpressionCompilationException;
51 import org.simantics.scl.compiler.types.Type;
52 import org.simantics.scl.compiler.types.Types;
53 import org.simantics.scl.runtime.SCLContext;
54 import org.simantics.scl.runtime.function.Function;
55 import org.simantics.scl.runtime.function.FunctionImpl2;
56 import org.simantics.scl.runtime.reporting.DelegatingSCLReportingHandler;
57 import org.simantics.scl.runtime.reporting.SCLReporting;
58 import org.simantics.scl.runtime.reporting.SCLReportingHandler;
59 import org.simantics.scl.runtime.tuple.Tuple0;
61 import gnu.trove.map.hash.THashMap;
62 import gnu.trove.procedure.TObjectProcedure;
63 import gnu.trove.set.hash.THashSet;
66 public class CommandSession {
68 ModuleRepository moduleRepository;
69 SCLReportingHandler defaultHandler;
71 RuntimeEnvironment runtimeEnvironment;
72 ValueToStringConverter valueToStringConverter;
74 ArrayList<CommandSessionImportEntry> importEntries = new ArrayList<CommandSessionImportEntry>();
76 THashMap<String,Object> variableValues = new THashMap<String,Object>();
77 THashMap<String,Type> variableTypes = new THashMap<String,Type>();
79 PrintStream fileOutput;
80 private UpdateListener dependenciesListener;
83 * Only checks the commands for compilation errors but does not run them.
85 private boolean validateOnly;
87 public CommandSession(ModuleRepository moduleRepository, SCLReportingHandler handler) {
88 this.moduleRepository = moduleRepository;
89 this.defaultHandler = new PrintDecorator(
90 handler == null ? SCLReportingHandler.DEFAULT : handler);
91 updateRuntimeEnvironment(true);
94 private static EnvironmentSpecification createEnvironmentSpecification(Collection<CommandSessionImportEntry> importEntries) {
95 EnvironmentSpecification spec = new EnvironmentSpecification();
96 spec.importModule("Builtin", "");
97 spec.importModule("StandardLibrary", "");
98 spec.importModule("Expressions/Context", null);
99 for(CommandSessionImportEntry entry : importEntries)
100 if(!entry.disabled && !entry.hasError)
101 spec.importModule(entry.moduleName, entry.localName);
105 public void updateRuntimeEnvironment(boolean clearErrorsFlags) {
107 for(CommandSessionImportEntry entry : importEntries)
108 entry.hasError = false;
109 EnvironmentSpecification environmentSpecification = createEnvironmentSpecification(importEntries);
111 runtimeEnvironment = null;
113 if(dependenciesListener != null)
114 dependenciesListener.stopListening();
116 runtimeEnvironment = moduleRepository.createRuntimeEnvironment(
117 environmentSpecification,
118 getClass().getClassLoader(),
119 dependenciesListener);
120 } catch(ImportFailureException e) {
121 THashSet<String> failedModules = new THashSet<String>();
122 for(ImportFailure failure : e.failures) {
123 failedModules.add(failure.moduleName);
124 defaultHandler.printError(failure.toString());
125 if(failure.reason instanceof CompilationError[])
126 for(CompilationError error : (CompilationError[])failure.reason) {
127 if(error.severity != ErrorSeverity.WARNING)
128 defaultHandler.printError(" " + error.description);
131 for(CommandSessionImportEntry entry : importEntries)
132 if(failedModules.contains(entry.moduleName))
133 entry.hasError = true;
134 environmentSpecification = createEnvironmentSpecification(importEntries);
136 runtimeEnvironment = moduleRepository.createRuntimeEnvironment(
137 environmentSpecification,
138 getClass().getClassLoader()); // no listener here, because should listen also failed modules
139 } catch (ImportFailureException e1) {
140 for(ImportFailure failure : e1.failures)
141 defaultHandler.printError(failure.toString());
144 } catch(RuntimeException e) {
148 valueToStringConverter = new ValueToStringConverter(runtimeEnvironment);
151 public RuntimeEnvironment getRuntimeEnvironment() {
152 return runtimeEnvironment;
155 public ModuleRepository getModuleRepository() {
156 return moduleRepository;
159 private static class CancelExecution extends RuntimeException {
160 private static final long serialVersionUID = -6925642906311538873L;
163 private LocalStorage localStorage = new LocalStorage() {
165 public void store(String name, Object value, Type type) {
166 variableValues.put(name, value);
167 variableTypes.put(name, type);
171 private static class LocalFunction {
172 final Function function;
175 public LocalFunction(Function function, Type type) {
176 this.function = function;
181 private static final THashMap<String, LocalFunction> LOCAL_FUNCTIONS = new THashMap<String, LocalFunction>();
183 LOCAL_FUNCTIONS.put("runFromFile", new LocalFunction(new FunctionImpl2<CommandSession, String, Tuple0>() {
185 public Tuple0 apply(final CommandSession commandSession, String fileName) {
186 SCLContext context = SCLContext.getCurrent();
187 commandSession.runFromFile(fileName, (SCLReportingHandler)context.get(SCLReportingHandler.REPORTING_HANDLER));
188 return Tuple0.INSTANCE;
190 }, Types.functionE(Types.STRING, Types.PROC, Types.UNIT)));
191 LOCAL_FUNCTIONS.put("runTest", new LocalFunction(new FunctionImpl2<CommandSession, String, Tuple0>() {
193 public Tuple0 apply(final CommandSession commandSession, String fileName) {
194 SCLContext context = SCLContext.getCurrent();
195 SCLReportingHandler handler = (SCLReportingHandler)context.get(SCLReportingHandler.REPORTING_HANDLER);
197 BufferedReader reader = new BufferedReader(new LaxUTF8Reader(fileName));
199 new TestScriptExecutor(commandSession, reader, handler).execute();
203 } catch(IOException e) {
204 handler.printError(e.getMessage());
206 return Tuple0.INSTANCE;
208 }, Types.functionE(Types.STRING, Types.PROC, Types.UNIT)));
209 LOCAL_FUNCTIONS.put("reset", new LocalFunction(new FunctionImpl2<CommandSession, Tuple0, Tuple0>() {
211 public Tuple0 apply(CommandSession commandSession, Tuple0 dummy) {
212 commandSession.removeTransientImports();
213 commandSession.removeVariables();
214 commandSession.moduleRepository.getSourceRepository().checkUpdates();
215 commandSession.updateRuntimeEnvironment(true);
216 return Tuple0.INSTANCE;
218 }, Types.functionE(Types.UNIT, Types.PROC, Types.UNIT)));
219 LOCAL_FUNCTIONS.put("variables", new LocalFunction(new FunctionImpl2<CommandSession, Tuple0, List<String>>() {
221 public List<String> apply(CommandSession commandSession, Tuple0 dummy) {
222 ArrayList<String> result = new ArrayList<String>(commandSession.variableTypes.keySet());
223 Collections.sort(result);
226 }, Types.functionE(Types.PUNIT, Types.PROC, Types.list(Types.STRING))));
227 LOCAL_FUNCTIONS.put("startPrintingToFile", new LocalFunction(new FunctionImpl2<CommandSession, String, Tuple0>() {
229 public Tuple0 apply(final CommandSession commandSession, String fileName) {
231 if(commandSession.fileOutput != null) {
232 commandSession.fileOutput.close();
233 SCLReporting.printError("Printing to file was already enabled. Stopped the previous printing.");
235 commandSession.fileOutput = new PrintStream(fileName, "UTF-8");
236 } catch (FileNotFoundException e) {
237 throw new RuntimeException(e);
238 } catch (UnsupportedEncodingException e) {
239 throw new RuntimeException(e);
241 return Tuple0.INSTANCE;
243 }, Types.functionE(Types.STRING, Types.PROC, Types.UNIT)));
244 LOCAL_FUNCTIONS.put("startAppendingToFile", new LocalFunction(new FunctionImpl2<CommandSession, String, Tuple0>() {
246 public Tuple0 apply(final CommandSession commandSession, String fileName) {
248 if(commandSession.fileOutput != null) {
249 commandSession.fileOutput.close();
250 SCLReporting.printError("Printing to file was already enabled. Stopped the previous printing.");
252 FileOutputStream stream = new FileOutputStream(fileName, true);
253 commandSession.fileOutput = new PrintStream(stream, false, "UTF-8");
254 } catch (FileNotFoundException e) {
255 throw new RuntimeException(e);
256 } catch (UnsupportedEncodingException e) {
257 throw new RuntimeException(e);
259 return Tuple0.INSTANCE;
261 }, Types.functionE(Types.STRING, Types.PROC, Types.UNIT)));
262 LOCAL_FUNCTIONS.put("stopPrintingToFile", new LocalFunction(new FunctionImpl2<CommandSession, Tuple0, Tuple0>() {
264 public Tuple0 apply(final CommandSession commandSession, Tuple0 dummy) {
265 if(commandSession.fileOutput != null) {
266 commandSession.fileOutput.close();
267 commandSession.fileOutput = null;
269 return Tuple0.INSTANCE;
271 }, Types.functionE(Types.PUNIT, Types.PROC, Types.UNIT)));
274 private LocalEnvironment createLocalEnvironment() {
275 return new AbstractLocalEnvironment() {
276 Variable contextVariable = new Variable("context", Names.Expressions_Context_Context);
278 public Expression resolve(Environment environment, String localName) {
279 Type type = variableTypes.get(localName);
282 new EConstant(environment.getValue(Names.Expressions_Context_contextGet), type),
283 new EVariable(contextVariable),
284 new ELiteral(new StringConstant(localName))
286 LocalFunction localFunction = LOCAL_FUNCTIONS.get(localName);
287 if(localFunction != null) {
288 return new EExternalConstant(
289 localFunction.function.apply(CommandSession.this),
295 protected Variable[] getContextVariables() {
296 return new Variable[] { contextVariable };
299 public void forNames(TObjectProcedure<String> proc) {
300 for(String name : variableTypes.keySet())
302 for(String name : LOCAL_FUNCTIONS.keySet())
308 protected void removeTransientImports() {
309 ArrayList<CommandSessionImportEntry> newEntries = new ArrayList<CommandSessionImportEntry>(importEntries.size());
310 for(CommandSessionImportEntry entry : importEntries)
312 newEntries.add(entry);
313 importEntries = newEntries;
316 public THashMap<String,Type> localNamesForContentProposals() {
317 THashMap<String,Type> result = new THashMap<String,Type>();
318 for(Map.Entry<String,LocalFunction> entry : LOCAL_FUNCTIONS.entrySet())
319 result.put(entry.getKey(), entry.getValue().type);
320 result.putAll(variableTypes);
324 private CompiledCommand compile(Expression expression) throws SCLExpressionCompilationException {
325 LocalEnvironment localEnvironment = createLocalEnvironment();
326 if(runtimeEnvironment == null)
327 throw new SCLExpressionCompilationException(new CompilationError[] {
328 new CompilationError("Compilation failed: imports in the current environment have failed.")
330 ExpressionEvaluator evaluator = new ExpressionEvaluator(runtimeEnvironment, localStorage, expression);
331 Function command = (Function)evaluator
332 .localEnvironment(localEnvironment)
333 .decorateExpression(true)
334 .validateOnly(validateOnly)
336 return new CompiledCommand(command, evaluator.getType());
339 class PrintDecorator extends DelegatingSCLReportingHandler {
340 public PrintDecorator(SCLReportingHandler baseHandler) {
345 public void print(String text) {
347 if(fileOutput != null)
348 fileOutput.println(text);
352 public void printCommand(String command) {
353 super.printCommand(command);
354 if(fileOutput != null)
355 fileOutput.println("> " + command);
359 public void printError(String error) {
360 super.printError(error);
361 if(fileOutput != null)
362 fileOutput.println(error);
366 @SuppressWarnings("unchecked")
367 private void execute(MemoReader reader, Expression expression, final SCLReportingHandler handler) {
368 SCLContext context = SCLContext.getCurrent();
369 Object oldPrinter = context.put(SCLReportingHandler.REPORTING_HANDLER, handler);
371 CompiledCommand command;
373 handler.printCommand(reader.extractString(expression.location));
374 command = compile(expression);
375 } catch (SCLExpressionCompilationException e) {
378 CompilationError[] errors = e.getErrors();
379 for(CompilationError error : errors) {
380 if(error.location != Locations.NO_LOCATION)
381 handler.printError(reader.locationUnderlining(error.location));
382 handler.printError(error.description);
384 throw new CancelExecution();
386 reader.forgetEverythingBefore(Locations.endOf(expression.location));
389 Object resultValue = command.command.apply(variableValues);
390 String resultString = toString(resultValue, command.type);
391 if(!resultString.isEmpty())
392 handler.print(resultString);
394 } catch(Exception e) {
397 if(!(e instanceof CancelExecution)) {
398 if(e instanceof InterruptedException)
399 handler.printError("Execution interrupted.");
401 formatException(handler, e);
403 throw new CancelExecution();
405 context.put(SCLReportingHandler.REPORTING_HANDLER, oldPrinter);
409 private String toString(Object value, Type type) {
410 if(type.equals(Types.UNIT))
413 return valueToStringConverter.show(value, type);
414 } catch (SCLExpressionCompilationException e) {
415 return "<value of type " + type + ">";
419 class CommandParser extends SCLParserImpl {
420 SCLReportingHandler handler;
422 public CommandParser(SCLReportingHandler handler, MemoReader reader) {
424 this.reader = reader;
425 this.handler = handler;
430 if(currentBlock != null) {
432 currentBlock.location = Locations.combine(
433 currentBlock.getFirst().location,
434 currentBlock.getLast().location);
435 execute(reader, currentBlock, handler);
440 protected Object reduceStatementCommand() {
441 Statement statement = (Statement)get(0);
442 if(statement.mayBeRecursive()) {
443 if(currentBlock == null)
444 currentBlock = new EBlock();
445 currentBlock.addStatement(statement);
450 if(statement instanceof GuardStatement)
451 execute(reader, ((GuardStatement)statement).value, handler);
453 EBlock block = new EBlock();
454 block.addStatement(statement);
455 block.location = statement.location;
456 execute(reader, block, handler);
463 protected Object reduceImportCommand() {
467 ImportDeclaration importDeclaration = (ImportDeclaration)get(0);
468 handler.printCommand(reader.extractString(importDeclaration.location));
469 new CommandSessionImportEntry(importDeclaration.moduleName,
470 importDeclaration.localName).addTo(importEntries);
471 updateRuntimeEnvironment(false);
476 private void checkInterrupted() {
477 if(Thread.interrupted()) {
478 defaultHandler.printError("Execution interrupted.");
479 throw new CancelExecution();
483 private CompilationError[] validate(Reader commandReader) {
484 CommandParser parser = new CommandParser(defaultHandler, new MemoReader(commandReader));
487 parser.parseCommands();
488 parser.finishBlock();
489 return CompilationError.EMPTY_ARRAY;
490 } catch(SCLExpressionCompilationException e) {
491 return e.getErrors();
492 } catch(SCLSyntaxErrorException e) {
493 return new CompilationError[] { new CompilationError(e.location, e.getMessage()) };
494 } catch(Exception e) {
495 return new CompilationError[] { new CompilationError(Locations.NO_LOCATION, e.getMessage()) };
497 validateOnly = false;
501 public void execute(Reader commandReader, SCLReportingHandler handler) {
503 handler = defaultHandler;
504 else if (!(handler instanceof PrintDecorator))
505 handler = new PrintDecorator(handler);
506 CommandParser parser = new CommandParser(handler, new MemoReader(commandReader));
508 parser.parseCommands();
509 parser.finishBlock();
510 } catch(CancelExecution e) {
511 } catch(SCLSyntaxErrorException e) {
512 handler.printCommand(parser.reader.getLastCommand());
513 if(e.location != Locations.NO_LOCATION)
514 handler.printError(parser.reader.locationUnderlining(e.location));
515 handler.printError(e.getMessage());
516 } catch (Exception | AssertionError e) {
517 if(e instanceof InterruptedException)
518 handler.printError("Execution interrupted.");
520 formatException(handler, e);
524 public void execute(String command) {
525 execute(new StringReader(command), null);
528 public void execute(String command, SCLReportingHandler handler) {
529 execute(new StringReader(command), handler);
532 private static final String THIS_CLASS_NAME = CommandSession.class.getName();
534 public static void formatException(
535 SCLReportingHandler handler,
537 formatException(handler, null, e);
540 private static void formatException(
541 SCLReportingHandler handler,
542 StackTraceElement[] enclosingTrace,
544 StackTraceElement[] elements = e.getStackTrace();
545 Throwable cause = e.getCause();
547 formatException(handler, elements, cause);
548 handler.printError("Rethrown as ");
550 handler.printError(e.toString());
551 int endPos = elements.length;
552 if(enclosingTrace != null) {
553 int p = enclosingTrace.length;
554 while(endPos > 0 && p > 0 && elements[endPos-1].equals(enclosingTrace[p-1])) {
560 for(int i=0;i<endPos;++i) {
561 StackTraceElement element = elements[i];
562 if(element.getMethodName().equals("execute") &&
563 element.getClassName().equals(THIS_CLASS_NAME)) {
566 element = elements[endPos-1];
567 String className = element.getClassName();
568 if(className.startsWith("org.simantics.scl.compiler.top.SCLExpressionCompiler")
569 //|| element.getClassName().startsWith("org.simantics.scl.compiler.interpreted.")
570 || className.startsWith("org.simantics.scl.runtime.function.FunctionImpl")
571 //|| className.startsWith("tempsclpackage")
581 for(int i=0;i<endPos;++i) {
582 StringBuilder b = new StringBuilder();
583 StackTraceElement element = elements[i];
584 String className = element.getClassName();
585 if(className.equals("org.simantics.scl.compiler.interpreted.IApply")
586 || className.equals("org.simantics.scl.compiler.interpreted.ILet")
587 || className.startsWith("tempsclpackage"))
589 if(className.startsWith("org.simantics.scl.compiler.interpreted.ILambda")) {
590 b.append("\tat command line\n");
593 String methodName = element.getMethodName();
594 if(className.startsWith("org.simantics.scl.runtime.function.FunctionImpl") &&
595 methodName.equals("applyArray"))
597 String fileName = element.getFileName();
598 if("_SCL_Closure".equals(fileName))
601 if("_SCL_Module".equals(fileName)
602 || "_SCL_TypeClassInstance".equals(fileName))
605 .append(NameMangling.demangle(methodName))
606 .append('(').append(element.getLineNumber()).append(')');
609 handler.printError(b.toString());
613 public void setVariable(String name, Type type, Object value) {
614 variableValues.put(name, value);
615 variableTypes.put(name, type);
618 public Object getVariableValue(String name) {
619 return variableValues.get(name);
622 public Type getVariableType(String name) {
623 return variableTypes.get(name);
626 public void removeVariable(String name) {
627 variableValues.remove(name);
628 variableTypes.remove(name);
631 public void removeVariables() {
632 variableValues.clear();
633 variableTypes.clear();
636 public Set<String> getVariables() {
637 return variableTypes.keySet();
640 public ArrayList<CommandSessionImportEntry> getImportEntries() {
641 return importEntries;
644 public void setImportEntries(
645 ArrayList<CommandSessionImportEntry> importEntries) {
646 this.importEntries = importEntries;
647 updateRuntimeEnvironment(true);
650 public void runFromFile(String fileName, SCLReportingHandler handler) {
652 Reader reader = new LaxUTF8Reader(fileName);
654 execute(reader, handler);
658 } catch(IOException e) {
659 formatException(handler, e);
663 public static CompilationError[] validate(ModuleRepository moduleRepository,StringReader commandReader) {
664 CommandSession session = new CommandSession(moduleRepository, null);
665 return session.validate(commandReader);
668 public static CompilationError[] validate(ModuleRepository moduleRepository,String command) {
669 return validate(moduleRepository, new StringReader(command));
672 public CompilationError[] validate(String command) {
673 return validate(new StringReader(command));
676 public void setDependenciesListener(UpdateListener dependenciesListener) {
677 this.dependenciesListener = dependenciesListener;