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.dynamic.SafeDynamic;
21 import org.simantics.scl.compiler.elaboration.expressions.EApply;
22 import org.simantics.scl.compiler.elaboration.expressions.EBlock;
23 import org.simantics.scl.compiler.elaboration.expressions.EConstant;
24 import org.simantics.scl.compiler.elaboration.expressions.EExternalConstant;
25 import org.simantics.scl.compiler.elaboration.expressions.ELiteral;
26 import org.simantics.scl.compiler.elaboration.expressions.EVariable;
27 import org.simantics.scl.compiler.elaboration.expressions.Expression;
28 import org.simantics.scl.compiler.elaboration.expressions.Variable;
29 import org.simantics.scl.compiler.elaboration.expressions.block.GuardStatement;
30 import org.simantics.scl.compiler.elaboration.expressions.block.Statement;
31 import org.simantics.scl.compiler.environment.AbstractLocalEnvironment;
32 import org.simantics.scl.compiler.environment.Environment;
33 import org.simantics.scl.compiler.environment.LocalEnvironment;
34 import org.simantics.scl.compiler.environment.specification.EnvironmentSpecification;
35 import org.simantics.scl.compiler.errors.CompilationError;
36 import org.simantics.scl.compiler.errors.ErrorSeverity;
37 import org.simantics.scl.compiler.errors.Locations;
38 import org.simantics.scl.compiler.internal.codegen.utils.NameMangling;
39 import org.simantics.scl.compiler.internal.parsing.exceptions.SCLSyntaxErrorException;
40 import org.simantics.scl.compiler.internal.parsing.parser.SCLParserImpl;
41 import org.simantics.scl.compiler.internal.parsing.utils.LaxUTF8Reader;
42 import org.simantics.scl.compiler.internal.parsing.utils.MemoReader;
43 import org.simantics.scl.compiler.module.ImportDeclaration;
44 import org.simantics.scl.compiler.module.repository.ImportFailure;
45 import org.simantics.scl.compiler.module.repository.ImportFailureException;
46 import org.simantics.scl.compiler.module.repository.ModuleRepository;
47 import org.simantics.scl.compiler.module.repository.UpdateListener;
48 import org.simantics.scl.compiler.runtime.RuntimeEnvironment;
49 import org.simantics.scl.compiler.top.ExpressionEvaluator;
50 import org.simantics.scl.compiler.top.LocalStorage;
51 import org.simantics.scl.compiler.top.SCLExpressionCompilationException;
52 import org.simantics.scl.compiler.types.Type;
53 import org.simantics.scl.compiler.types.Types;
54 import org.simantics.scl.runtime.SCLContext;
55 import org.simantics.scl.runtime.function.Function;
56 import org.simantics.scl.runtime.function.FunctionImpl2;
57 import org.simantics.scl.runtime.reporting.DelegatingSCLReportingHandler;
58 import org.simantics.scl.runtime.reporting.SCLReporting;
59 import org.simantics.scl.runtime.reporting.SCLReportingHandler;
60 import org.simantics.scl.runtime.tuple.Tuple0;
62 import gnu.trove.map.hash.THashMap;
63 import gnu.trove.procedure.TObjectProcedure;
64 import gnu.trove.set.hash.THashSet;
67 public class CommandSession {
69 ModuleRepository moduleRepository;
70 SCLReportingHandler defaultHandler;
72 RuntimeEnvironment runtimeEnvironment;
73 ValueToStringConverter valueToStringConverter;
75 ArrayList<CommandSessionImportEntry> importEntries = new ArrayList<CommandSessionImportEntry>();
77 THashMap<String,Object> variableValues = new THashMap<String,Object>();
78 THashMap<String,Type> variableTypes = new THashMap<String,Type>();
80 PrintStream fileOutput;
81 private UpdateListener dependenciesListener;
84 * Only checks the commands for compilation errors but does not run them.
86 private boolean validateOnly;
88 public CommandSession(ModuleRepository moduleRepository) {
89 this(moduleRepository, SCLReporting.getCurrentReportingHandler());
92 public CommandSession(ModuleRepository moduleRepository, SCLReportingHandler handler) {
93 this.moduleRepository = moduleRepository;
94 this.defaultHandler = new PrintDecorator(
95 handler == null ? SCLReportingHandler.DEFAULT : handler);
96 updateRuntimeEnvironment(true);
99 private static EnvironmentSpecification createEnvironmentSpecification(Collection<CommandSessionImportEntry> importEntries) {
100 EnvironmentSpecification spec = new EnvironmentSpecification();
101 spec.importModule("Builtin", "");
102 spec.importModule("StandardLibrary", "");
103 spec.importModule("Expressions/Context", null);
104 for(CommandSessionImportEntry entry : importEntries)
105 if(!entry.disabled && !entry.hasError)
106 spec.importModule(entry.moduleName, entry.localName);
110 public void updateRuntimeEnvironment(boolean clearErrorsFlags) {
112 for(CommandSessionImportEntry entry : importEntries)
113 entry.hasError = false;
114 EnvironmentSpecification environmentSpecification = createEnvironmentSpecification(importEntries);
116 runtimeEnvironment = null;
118 if(dependenciesListener != null)
119 dependenciesListener.stopListening();
121 runtimeEnvironment = moduleRepository.createRuntimeEnvironment(
122 environmentSpecification,
123 getClass().getClassLoader(),
124 dependenciesListener);
125 } catch(ImportFailureException e) {
126 THashSet<String> failedModules = new THashSet<String>();
127 for(ImportFailure failure : e.failures) {
128 failedModules.add(failure.moduleName);
129 defaultHandler.printError(failure.toString());
130 if(failure.reason instanceof CompilationError[])
131 for(CompilationError error : (CompilationError[])failure.reason) {
132 if(error.severity != ErrorSeverity.WARNING)
133 defaultHandler.printError(" " + error.description);
136 for(CommandSessionImportEntry entry : importEntries)
137 if(failedModules.contains(entry.moduleName))
138 entry.hasError = true;
139 environmentSpecification = createEnvironmentSpecification(importEntries);
141 runtimeEnvironment = moduleRepository.createRuntimeEnvironment(
142 environmentSpecification,
143 getClass().getClassLoader()); // no listener here, because should listen also failed modules
144 } catch (ImportFailureException e1) {
145 for(ImportFailure failure : e1.failures)
146 defaultHandler.printError(failure.toString());
149 } catch(RuntimeException e) {
153 valueToStringConverter = new ValueToStringConverter(runtimeEnvironment);
156 public RuntimeEnvironment getRuntimeEnvironment() {
157 return runtimeEnvironment;
160 public ModuleRepository getModuleRepository() {
161 return moduleRepository;
164 private static class CancelExecution extends RuntimeException {
165 private static final long serialVersionUID = -6925642906311538873L;
168 private LocalStorage localStorage = new LocalStorage() {
170 public void store(String name, Object value, Type type) {
171 variableValues.put(name, value);
172 variableTypes.put(name, type);
176 private static class LocalFunction {
177 final Function function;
180 public LocalFunction(Function function, Type type) {
181 this.function = function;
186 private static final THashMap<String, LocalFunction> LOCAL_FUNCTIONS = new THashMap<String, LocalFunction>();
188 LOCAL_FUNCTIONS.put("runFromFile", new LocalFunction(new FunctionImpl2<CommandSession, String, Tuple0>() {
190 public Tuple0 apply(final CommandSession commandSession, String fileName) {
191 SCLContext context = SCLContext.getCurrent();
192 commandSession.runFromFile(fileName, (SCLReportingHandler)context.get(SCLReportingHandler.REPORTING_HANDLER));
193 return Tuple0.INSTANCE;
195 }, Types.functionE(Types.STRING, Types.PROC, Types.UNIT)));
196 LOCAL_FUNCTIONS.put("runTest", new LocalFunction(new FunctionImpl2<CommandSession, String, Tuple0>() {
198 public Tuple0 apply(final CommandSession commandSession, String fileName) {
199 SCLContext context = SCLContext.getCurrent();
200 SCLReportingHandler handler = (SCLReportingHandler)context.get(SCLReportingHandler.REPORTING_HANDLER);
202 BufferedReader reader = new BufferedReader(new LaxUTF8Reader(fileName));
204 new TestScriptExecutor(commandSession, reader, handler).execute();
208 } catch(IOException e) {
209 handler.printError(e.getMessage());
211 return Tuple0.INSTANCE;
213 }, Types.functionE(Types.STRING, Types.PROC, Types.UNIT)));
214 LOCAL_FUNCTIONS.put("reset", new LocalFunction(new FunctionImpl2<CommandSession, Tuple0, Tuple0>() {
216 public Tuple0 apply(CommandSession commandSession, Tuple0 dummy) {
217 commandSession.removeTransientImports();
218 commandSession.removeVariables();
219 commandSession.moduleRepository.getSourceRepository().checkUpdates();
220 commandSession.updateRuntimeEnvironment(true);
221 return Tuple0.INSTANCE;
223 }, Types.functionE(Types.UNIT, Types.PROC, Types.UNIT)));
224 LOCAL_FUNCTIONS.put("variables", new LocalFunction(new FunctionImpl2<CommandSession, Tuple0, List<String>>() {
226 public List<String> apply(CommandSession commandSession, Tuple0 dummy) {
227 ArrayList<String> result = new ArrayList<String>(commandSession.variableTypes.keySet());
228 Collections.sort(result);
231 }, Types.functionE(Types.PUNIT, Types.PROC, Types.list(Types.STRING))));
232 LOCAL_FUNCTIONS.put("startPrintingToFile", new LocalFunction(new FunctionImpl2<CommandSession, String, Tuple0>() {
234 public Tuple0 apply(final CommandSession commandSession, String fileName) {
236 if(commandSession.fileOutput != null) {
237 commandSession.fileOutput.close();
238 SCLReporting.printError("Printing to file was already enabled. Stopped the previous printing.");
240 commandSession.fileOutput = new PrintStream(fileName, "UTF-8");
241 } catch (FileNotFoundException e) {
242 throw new RuntimeException(e);
243 } catch (UnsupportedEncodingException e) {
244 throw new RuntimeException(e);
246 return Tuple0.INSTANCE;
248 }, Types.functionE(Types.STRING, Types.PROC, Types.UNIT)));
249 LOCAL_FUNCTIONS.put("startAppendingToFile", new LocalFunction(new FunctionImpl2<CommandSession, String, Tuple0>() {
251 public Tuple0 apply(final CommandSession commandSession, String fileName) {
253 if(commandSession.fileOutput != null) {
254 commandSession.fileOutput.close();
255 SCLReporting.printError("Printing to file was already enabled. Stopped the previous printing.");
257 FileOutputStream stream = new FileOutputStream(fileName, true);
258 commandSession.fileOutput = new PrintStream(stream, false, "UTF-8");
259 } catch (FileNotFoundException e) {
260 throw new RuntimeException(e);
261 } catch (UnsupportedEncodingException e) {
262 throw new RuntimeException(e);
264 return Tuple0.INSTANCE;
266 }, Types.functionE(Types.STRING, Types.PROC, Types.UNIT)));
267 LOCAL_FUNCTIONS.put("stopPrintingToFile", new LocalFunction(new FunctionImpl2<CommandSession, Tuple0, Tuple0>() {
269 public Tuple0 apply(final CommandSession commandSession, Tuple0 dummy) {
270 if(commandSession.fileOutput != null) {
271 commandSession.fileOutput.close();
272 commandSession.fileOutput = null;
274 return Tuple0.INSTANCE;
276 }, Types.functionE(Types.PUNIT, Types.PROC, Types.UNIT)));
279 private LocalEnvironment createLocalEnvironment() {
280 return new AbstractLocalEnvironment() {
281 Variable contextVariable = new Variable("context", Names.Expressions_Context_Context);
283 public Expression resolve(Environment environment, String localName) {
284 Type type = variableTypes.get(localName);
287 new EConstant(environment.getValue(Names.Expressions_Context_contextGet), type),
288 new EVariable(contextVariable),
289 new ELiteral(new StringConstant(localName))
291 LocalFunction localFunction = LOCAL_FUNCTIONS.get(localName);
292 if(localFunction != null) {
293 return new EExternalConstant(
294 localFunction.function.apply(CommandSession.this),
300 protected Variable[] getContextVariables() {
301 return new Variable[] { contextVariable };
304 public void forNames(TObjectProcedure<String> proc) {
305 for(String name : variableTypes.keySet())
307 for(String name : LOCAL_FUNCTIONS.keySet())
313 protected void removeTransientImports() {
314 ArrayList<CommandSessionImportEntry> newEntries = new ArrayList<CommandSessionImportEntry>(importEntries.size());
315 for(CommandSessionImportEntry entry : importEntries)
317 newEntries.add(entry);
318 importEntries = newEntries;
321 public THashMap<String,Type> localNamesForContentProposals() {
322 THashMap<String,Type> result = new THashMap<String,Type>();
323 for(Map.Entry<String,LocalFunction> entry : LOCAL_FUNCTIONS.entrySet())
324 result.put(entry.getKey(), entry.getValue().type);
325 result.putAll(variableTypes);
329 private CompiledCommand compile(Expression expression) throws SCLExpressionCompilationException {
330 LocalEnvironment localEnvironment = createLocalEnvironment();
331 if(runtimeEnvironment == null)
332 throw new SCLExpressionCompilationException(new CompilationError[] {
333 new CompilationError("Compilation failed: imports in the current environment have failed.")
335 ExpressionEvaluator evaluator = new ExpressionEvaluator(runtimeEnvironment, localStorage, expression);
336 Function command = (Function)evaluator
337 .localEnvironment(localEnvironment)
338 .decorateExpression(true)
339 .validateOnly(validateOnly)
341 return new CompiledCommand(command, evaluator.getType());
344 class PrintDecorator extends DelegatingSCLReportingHandler {
345 public PrintDecorator(SCLReportingHandler baseHandler) {
350 public void print(String text) {
352 if(fileOutput != null)
353 fileOutput.println(text);
357 public void printCommand(String command) {
358 super.printCommand(command);
359 if(fileOutput != null)
360 fileOutput.println("> " + command);
364 public void printError(String error) {
365 super.printError(error);
366 if(fileOutput != null)
367 fileOutput.println(error);
371 @SuppressWarnings("unchecked")
372 private void execute(MemoReader reader, Expression expression, final SCLReportingHandler handler) {
373 SCLContext context = SCLContext.getCurrent();
374 Object oldPrinter = context.put(SCLReportingHandler.REPORTING_HANDLER, handler);
376 CompiledCommand command;
378 handler.printCommand(reader.extractString(expression.location));
379 command = compile(expression);
380 } catch (SCLExpressionCompilationException e) {
383 CompilationError[] errors = e.getErrors();
384 for(CompilationError error : errors) {
385 if(error.location != Locations.NO_LOCATION)
386 handler.printError(reader.locationUnderlining(error.location));
387 handler.printError(error.description);
389 throw new CancelExecution();
391 reader.forgetEverythingBefore(Locations.endOf(expression.location));
394 Object resultValue = command.command.apply(variableValues);
395 String resultString = toString(resultValue, command.type);
396 if(!resultString.isEmpty())
397 handler.print(resultString);
399 } catch(Exception e) {
402 if(!(e instanceof CancelExecution)) {
403 if(e instanceof InterruptedException)
404 handler.printError("Execution interrupted.");
406 formatException(handler, e);
408 throw new CancelExecution();
410 context.put(SCLReportingHandler.REPORTING_HANDLER, oldPrinter);
414 private String toString(Object value, Type type) {
415 if(type.equals(Types.UNIT))
418 return valueToStringConverter.show(value, type);
419 } catch (SCLExpressionCompilationException e) {
420 return "<value of type " + type + ">";
424 class CommandParser extends SCLParserImpl {
425 SCLReportingHandler handler;
427 public CommandParser(SCLReportingHandler handler, MemoReader reader) {
429 this.reader = reader;
430 this.handler = handler;
435 if(currentBlock != null) {
437 currentBlock.location = Locations.combine(
438 currentBlock.getFirst().location,
439 currentBlock.getLast().location);
440 execute(reader, currentBlock, handler);
445 protected Object reduceStatementCommand() {
446 Statement statement = (Statement)get(0);
447 if(statement.mayBeRecursive()) {
448 if(currentBlock == null)
449 currentBlock = new EBlock();
450 currentBlock.addStatement(statement);
455 if(statement instanceof GuardStatement)
456 execute(reader, ((GuardStatement)statement).value, handler);
458 EBlock block = new EBlock();
459 block.addStatement(statement);
460 block.location = statement.location;
461 execute(reader, block, handler);
468 protected Object reduceImportCommand() {
472 ImportDeclaration importDeclaration = (ImportDeclaration)get(0);
473 handler.printCommand(reader.extractString(importDeclaration.location));
474 new CommandSessionImportEntry(importDeclaration.moduleName,
475 importDeclaration.localName).addTo(importEntries);
476 updateRuntimeEnvironment(false);
481 private void checkInterrupted() {
482 if(Thread.interrupted()) {
483 defaultHandler.printError("Execution interrupted.");
484 throw new CancelExecution();
488 private CompilationError[] validate(Reader commandReader) {
489 CommandParser parser = new CommandParser(defaultHandler, new MemoReader(commandReader));
492 parser.parseCommands();
493 parser.finishBlock();
494 return CompilationError.EMPTY_ARRAY;
495 } catch(SCLExpressionCompilationException e) {
496 return e.getErrors();
497 } catch(SCLSyntaxErrorException e) {
498 return new CompilationError[] { new CompilationError(e.location, e.getMessage()) };
499 } catch(Exception e) {
500 return new CompilationError[] { new CompilationError(Locations.NO_LOCATION, e.getMessage()) };
502 validateOnly = false;
506 public void execute(Reader commandReader, SCLReportingHandler handler) {
508 handler = defaultHandler;
509 else if (!(handler instanceof PrintDecorator))
510 handler = new PrintDecorator(handler);
511 CommandParser parser = new CommandParser(handler, new MemoReader(commandReader));
513 parser.parseCommands();
514 parser.finishBlock();
515 } catch(CancelExecution e) {
516 } catch(SCLSyntaxErrorException e) {
517 handler.printCommand(parser.reader.getLastCommand());
518 if(e.location != Locations.NO_LOCATION)
519 handler.printError(parser.reader.locationUnderlining(e.location));
520 handler.printError(e.getMessage());
521 } catch (Exception | AssertionError e) {
522 if(e instanceof InterruptedException)
523 handler.printError("Execution interrupted.");
525 formatException(handler, e);
529 public void execute(String command) {
530 execute(new StringReader(command), null);
533 public void execute(String command, SCLReportingHandler handler) {
534 execute(new StringReader(command), handler);
537 private static final String THIS_CLASS_NAME = CommandSession.class.getName();
539 public static void formatException(
540 SCLReportingHandler handler,
542 formatException(handler, null, e);
545 private static void formatException(
546 SCLReportingHandler handler,
547 StackTraceElement[] enclosingTrace,
549 StackTraceElement[] elements = e.getStackTrace();
550 Throwable cause = e.getCause();
552 formatException(handler, elements, cause);
553 handler.printError("Rethrown as ");
555 handler.printError(e.toString());
556 int endPos = elements.length;
557 if(enclosingTrace != null) {
558 int p = enclosingTrace.length;
559 while(endPos > 0 && p > 0 && elements[endPos-1].equals(enclosingTrace[p-1])) {
565 for(int i=0;i<endPos;++i) {
566 StackTraceElement element = elements[i];
567 if(element.getMethodName().equals("execute") &&
568 element.getClassName().equals(THIS_CLASS_NAME)) {
571 element = elements[endPos-1];
572 String className = element.getClassName();
573 if(className.startsWith("org.simantics.scl.compiler.top.SCLExpressionCompiler")
574 //|| element.getClassName().startsWith("org.simantics.scl.compiler.interpreted.")
575 || className.startsWith("org.simantics.scl.runtime.function.FunctionImpl")
576 //|| className.startsWith("tempsclpackage")
586 for(int i=0;i<endPos;++i) {
587 StringBuilder b = new StringBuilder();
588 StackTraceElement element = elements[i];
589 String className = element.getClassName();
590 if(className.equals("org.simantics.scl.compiler.interpreted.IApply")
591 || className.equals("org.simantics.scl.compiler.interpreted.ILet")
592 || className.startsWith("tempsclpackage"))
594 if(className.startsWith("org.simantics.scl.compiler.interpreted.ILambda")) {
595 b.append("\tat command line\n");
598 String methodName = element.getMethodName();
599 if(className.startsWith("org.simantics.scl.runtime.function.FunctionImpl") &&
600 methodName.equals("applyArray"))
602 String fileName = element.getFileName();
603 if("_SCL_Closure".equals(fileName))
606 if("_SCL_Module".equals(fileName)
607 || "_SCL_TypeClassInstance".equals(fileName))
610 .append(NameMangling.demangle(methodName))
611 .append('(').append(element.getLineNumber()).append(')');
614 handler.printError(b.toString());
618 public void setVariable(String name, Type type, Object value) {
619 variableValues.put(name, value);
620 variableTypes.put(name, type);
623 public void setVariable(String name, SafeDynamic typeAndValue) {
624 variableValues.put(name, typeAndValue.value);
625 variableTypes.put(name, typeAndValue.type_);
628 public Object getVariableValue(String name) {
629 return variableValues.get(name);
632 public Type getVariableType(String name) {
633 return variableTypes.get(name);
636 public SafeDynamic getVariableValueAndType(String name) {
637 Type type = variableTypes.get(name);
640 Object value = variableValues.get(name);
641 return new SafeDynamic(type, value);
644 public void removeVariable(String name) {
645 variableValues.remove(name);
646 variableTypes.remove(name);
649 public void removeVariables() {
650 variableValues.clear();
651 variableTypes.clear();
654 public Set<String> getVariables() {
655 return variableTypes.keySet();
658 public ArrayList<CommandSessionImportEntry> getImportEntries() {
659 return importEntries;
662 public void setImportEntries(
663 ArrayList<CommandSessionImportEntry> importEntries) {
664 this.importEntries = importEntries;
665 updateRuntimeEnvironment(true);
668 public void runFromFile(String fileName, SCLReportingHandler handler) {
670 Reader reader = new LaxUTF8Reader(fileName);
672 execute(reader, handler);
676 } catch(IOException e) {
677 formatException(handler, e);
681 public static CompilationError[] validate(ModuleRepository moduleRepository,StringReader commandReader) {
682 CommandSession session = new CommandSession(moduleRepository, null);
683 return session.validate(commandReader);
686 public static CompilationError[] validate(ModuleRepository moduleRepository,String command) {
687 return validate(moduleRepository, new StringReader(command));
690 public CompilationError[] validate(String command) {
691 return validate(new StringReader(command));
694 public void setDependenciesListener(UpdateListener dependenciesListener) {
695 this.dependenciesListener = dependenciesListener;