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.LinkedList;
15 import java.util.List;
19 import org.simantics.scl.compiler.common.names.Names;
20 import org.simantics.scl.compiler.constants.StringConstant;
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.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.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;
80 public CommandSession(ModuleRepository moduleRepository, SCLReportingHandler handler) {
81 this.moduleRepository = moduleRepository;
82 this.defaultHandler = new PrintDecorator(
83 handler == null ? SCLReportingHandler.DEFAULT : handler);
84 updateRuntimeEnvironment(true);
87 private static EnvironmentSpecification createEnvironmentSpecification(Collection<CommandSessionImportEntry> importEntries) {
88 EnvironmentSpecification spec = new EnvironmentSpecification();
89 spec.importModule("Builtin", "");
90 spec.importModule("StandardLibrary", "");
91 spec.importModule("Expressions/Context", null);
92 for(CommandSessionImportEntry entry : importEntries)
93 if(!entry.disabled && !entry.hasError)
94 spec.importModule(entry.moduleName, entry.localName);
98 public void updateRuntimeEnvironment(boolean clearErrorsFlags) {
100 for(CommandSessionImportEntry entry : importEntries)
101 entry.hasError = false;
102 EnvironmentSpecification environmentSpecification = createEnvironmentSpecification(importEntries);
104 runtimeEnvironment = null;
107 runtimeEnvironment = moduleRepository.createRuntimeEnvironment(
108 environmentSpecification,
109 getClass().getClassLoader());
110 } catch(ImportFailureException e) {
111 THashSet<String> failedModules = new THashSet<String>();
112 for(ImportFailure failure : e.failures) {
113 failedModules.add(failure.moduleName);
114 defaultHandler.printError(failure.toString());
115 if(failure.reason instanceof CompilationError[])
116 for(CompilationError error : (CompilationError[])failure.reason) {
117 defaultHandler.printError(" " + error.description);
120 for(CommandSessionImportEntry entry : importEntries)
121 if(failedModules.contains(entry.moduleName))
122 entry.hasError = true;
123 environmentSpecification = createEnvironmentSpecification(importEntries);
125 runtimeEnvironment = moduleRepository.createRuntimeEnvironment(
126 environmentSpecification,
127 getClass().getClassLoader());
128 } catch (ImportFailureException e1) {
129 for(ImportFailure failure : e1.failures)
130 defaultHandler.printError(failure.toString());
133 } catch(RuntimeException e) {
137 valueToStringConverter = new ValueToStringConverter(runtimeEnvironment);
140 public RuntimeEnvironment getRuntimeEnvironment() {
141 return runtimeEnvironment;
144 public ModuleRepository getModuleRepository() {
145 return moduleRepository;
148 private static class CancelExecution extends RuntimeException {
149 private static final long serialVersionUID = -6925642906311538873L;
152 private LocalStorage localStorage = new LocalStorage() {
154 public void store(String name, Object value, Type type) {
155 variableValues.put(name, value);
156 variableTypes.put(name, type);
160 private static class LocalFunction {
161 final Function function;
164 public LocalFunction(Function function, Type type) {
165 this.function = function;
170 private static final THashMap<String, LocalFunction> LOCAL_FUNCTIONS = new THashMap<String, LocalFunction>();
172 LOCAL_FUNCTIONS.put("runFromFile", new LocalFunction(new FunctionImpl2<CommandSession, String, Tuple0>() {
174 public Tuple0 apply(final CommandSession commandSession, String fileName) {
175 SCLContext context = SCLContext.getCurrent();
176 commandSession.runFromFile(fileName, (SCLReportingHandler)context.get(SCLReportingHandler.REPORTING_HANDLER));
177 return Tuple0.INSTANCE;
179 }, Types.functionE(Types.STRING, Types.PROC, Types.UNIT)));
180 LOCAL_FUNCTIONS.put("runTest", new LocalFunction(new FunctionImpl2<CommandSession, String, Tuple0>() {
182 public Tuple0 apply(final CommandSession commandSession, String fileName) {
183 SCLContext context = SCLContext.getCurrent();
184 SCLReportingHandler handler = (SCLReportingHandler)context.get(SCLReportingHandler.REPORTING_HANDLER);
186 BufferedReader reader = new BufferedReader(new LaxUTF8Reader(fileName));
188 new TestScriptExecutor(commandSession, reader, handler).execute();
192 } catch(IOException e) {
193 handler.printError(e.getMessage());
195 return Tuple0.INSTANCE;
197 }, Types.functionE(Types.STRING, Types.PROC, Types.UNIT)));
198 LOCAL_FUNCTIONS.put("reset", new LocalFunction(new FunctionImpl2<CommandSession, Tuple0, Tuple0>() {
200 public Tuple0 apply(CommandSession commandSession, Tuple0 dummy) {
201 commandSession.removeTransientImports();
202 commandSession.removeVariables();
203 commandSession.moduleRepository.getSourceRepository().checkUpdates();
204 commandSession.updateRuntimeEnvironment(true);
205 return Tuple0.INSTANCE;
207 }, Types.functionE(Types.UNIT, Types.PROC, Types.UNIT)));
208 LOCAL_FUNCTIONS.put("variables", new LocalFunction(new FunctionImpl2<CommandSession, Tuple0, List<String>>() {
210 public List<String> apply(CommandSession commandSession, Tuple0 dummy) {
211 ArrayList<String> result = new ArrayList<String>(commandSession.variableTypes.keySet());
212 Collections.sort(result);
215 }, Types.functionE(Types.PUNIT, Types.PROC, Types.list(Types.STRING))));
216 LOCAL_FUNCTIONS.put("startPrintingToFile", new LocalFunction(new FunctionImpl2<CommandSession, String, Tuple0>() {
218 public Tuple0 apply(final CommandSession commandSession, String fileName) {
220 if(commandSession.fileOutput != null) {
221 commandSession.fileOutput.close();
222 SCLReporting.printError("Printing to file was already enabled. Stopped the previous printing.");
224 commandSession.fileOutput = new PrintStream(fileName, "UTF-8");
225 } catch (FileNotFoundException e) {
226 throw new RuntimeException(e);
227 } catch (UnsupportedEncodingException e) {
228 throw new RuntimeException(e);
230 return Tuple0.INSTANCE;
232 }, Types.functionE(Types.STRING, Types.PROC, Types.UNIT)));
233 LOCAL_FUNCTIONS.put("startAppendingToFile", new LocalFunction(new FunctionImpl2<CommandSession, String, Tuple0>() {
235 public Tuple0 apply(final CommandSession commandSession, String fileName) {
237 if(commandSession.fileOutput != null) {
238 commandSession.fileOutput.close();
239 SCLReporting.printError("Printing to file was already enabled. Stopped the previous printing.");
241 FileOutputStream stream = new FileOutputStream(fileName, true);
242 commandSession.fileOutput = new PrintStream(stream, false, "UTF-8");
243 } catch (FileNotFoundException e) {
244 throw new RuntimeException(e);
245 } catch (UnsupportedEncodingException e) {
246 throw new RuntimeException(e);
248 return Tuple0.INSTANCE;
250 }, Types.functionE(Types.STRING, Types.PROC, Types.UNIT)));
251 LOCAL_FUNCTIONS.put("stopPrintingToFile", new LocalFunction(new FunctionImpl2<CommandSession, Tuple0, Tuple0>() {
253 public Tuple0 apply(final CommandSession commandSession, Tuple0 dummy) {
254 if(commandSession.fileOutput != null) {
255 commandSession.fileOutput.close();
256 commandSession.fileOutput = null;
258 return Tuple0.INSTANCE;
260 }, Types.functionE(Types.PUNIT, Types.PROC, Types.UNIT)));
263 private LocalEnvironment createLocalEnvironment() {
264 return new AbstractLocalEnvironment() {
265 Variable contextVariable = new Variable("context", Names.Expressions_Context_Context);
267 public Expression resolve(Environment environment, String localName) {
268 Type type = variableTypes.get(localName);
271 new EConstant(environment.getValue(Names.Expressions_Context_contextGet), type),
272 new EVariable(contextVariable),
273 new ELiteral(new StringConstant(localName))
275 LocalFunction localFunction = LOCAL_FUNCTIONS.get(localName);
276 if(localFunction != null) {
277 return new EExternalConstant(
278 localFunction.function.apply(CommandSession.this),
284 protected Variable[] getContextVariables() {
285 return new Variable[] { contextVariable };
288 public void forNames(TObjectProcedure<String> proc) {
289 for(String name : variableTypes.keySet())
291 for(String name : LOCAL_FUNCTIONS.keySet())
297 protected void removeTransientImports() {
298 ArrayList<CommandSessionImportEntry> newEntries = new ArrayList<CommandSessionImportEntry>(importEntries.size());
299 for(CommandSessionImportEntry entry : importEntries)
301 newEntries.add(entry);
302 importEntries = newEntries;
305 public THashMap<String,Type> localNamesForContentProposals() {
306 THashMap<String,Type> result = new THashMap<String,Type>();
307 for(Map.Entry<String,LocalFunction> entry : LOCAL_FUNCTIONS.entrySet())
308 result.put(entry.getKey(), entry.getValue().type);
309 result.putAll(variableTypes);
313 private CompiledCommand compile(Expression expression) throws SCLExpressionCompilationException {
314 LocalEnvironment localEnvironment = createLocalEnvironment();
315 if(runtimeEnvironment == null)
316 throw new SCLExpressionCompilationException(new CompilationError[] {
317 new CompilationError("Compilation failed: imports in the current environment have failed.")
319 ExpressionEvaluator evaluator = new ExpressionEvaluator(runtimeEnvironment, localStorage, expression);
320 Function command = (Function)evaluator
321 .localEnvironment(localEnvironment)
322 .decorateExpression(true)
324 return new CompiledCommand(command, evaluator.getType());
327 class PrintDecorator extends DelegatingSCLReportingHandler {
328 public PrintDecorator(SCLReportingHandler baseHandler) {
333 public void print(String text) {
335 if(fileOutput != null)
336 fileOutput.println(text);
340 public void printCommand(String command) {
341 super.printCommand(command);
342 if(fileOutput != null)
343 fileOutput.println("> " + command);
347 public void printError(String error) {
348 super.printError(error);
349 if(fileOutput != null)
350 fileOutput.println(error);
354 @SuppressWarnings("unchecked")
355 private void execute(MemoReader reader, Expression expression, final SCLReportingHandler handler) {
356 SCLContext context = SCLContext.getCurrent();
357 Object oldPrinter = context.put(SCLReportingHandler.REPORTING_HANDLER, handler);
359 CompiledCommand command;
361 handler.printCommand(reader.extractString(expression.location));
362 command = compile(expression);
363 } catch (SCLExpressionCompilationException e) {
364 CompilationError[] errors = ((SCLExpressionCompilationException)e).getErrors();
365 for(CompilationError error : errors) {
366 if(error.location != Locations.NO_LOCATION)
367 handler.printError(reader.locationUnderlining(error.location));
368 handler.printError(error.description);
370 throw new CancelExecution();
372 reader.forgetEverythingBefore(Locations.endOf(expression.location));
374 Object resultValue = command.command.apply(variableValues);
375 String resultString = toString(resultValue, command.type);
376 if(!resultString.isEmpty())
377 handler.print(resultString);
378 } catch(Exception e) {
379 if(!(e instanceof CancelExecution)) {
380 if(e instanceof InterruptedException)
381 handler.printError("Execution interrupted.");
383 formatException(handler, e);
385 throw new CancelExecution();
387 context.put(SCLReportingHandler.REPORTING_HANDLER, oldPrinter);
391 private String toString(Object value, Type type) {
392 if(type.equals(Types.UNIT))
395 return valueToStringConverter.show(value, type);
396 } catch (SCLExpressionCompilationException e) {
397 return "<value of type " + type + ">";
401 class CommandParser extends SCLParserImpl {
402 SCLReportingHandler handler;
404 public CommandParser(SCLReportingHandler handler, MemoReader reader) {
406 this.reader = reader;
407 this.handler = handler;
412 if(currentBlock != null) {
414 LinkedList<Statement> statements = currentBlock.getStatements();
415 currentBlock.location = Locations.combine(
416 statements.getFirst().location,
417 statements.getLast().location);
418 execute(reader, currentBlock, handler);
423 protected Object reduceStatementCommand() {
424 Statement statement = (Statement)get(0);
425 if(statement.mayBeRecursive()) {
426 if(currentBlock == null)
427 currentBlock = new EBlock();
428 currentBlock.addStatement(statement);
433 if(statement instanceof GuardStatement)
434 execute(reader, ((GuardStatement)statement).value, handler);
436 EBlock block = new EBlock();
437 block.addStatement(statement);
438 block.location = statement.location;
439 execute(reader, block, handler);
446 protected Object reduceImportCommand() {
450 ImportDeclaration importDeclaration = (ImportDeclaration)get(0);
451 handler.printCommand(reader.extractString(importDeclaration.location));
452 new CommandSessionImportEntry(importDeclaration.moduleName,
453 importDeclaration.localName).addTo(importEntries);
454 updateRuntimeEnvironment(false);
459 private void checkInterrupted() {
460 if(Thread.interrupted()) {
461 defaultHandler.printError("Execution interrupted.");
462 throw new CancelExecution();
466 public void execute(Reader commandReader, SCLReportingHandler handler) {
468 handler = defaultHandler;
469 else if (!(handler instanceof PrintDecorator))
470 handler = new PrintDecorator(handler);
471 CommandParser parser = new CommandParser(handler, new MemoReader(commandReader));
473 parser.parseCommands();
474 parser.finishBlock();
475 } catch(CancelExecution e) {
476 } catch(SCLSyntaxErrorException e) {
477 handler.printCommand(parser.reader.getLastCommand());
478 if(e.location != Locations.NO_LOCATION)
479 handler.printError(parser.reader.locationUnderlining(e.location));
480 handler.printError(e.getMessage());
481 } catch(Exception e) {
482 if(e instanceof InterruptedException)
483 handler.printError("Execution interrupted.");
485 formatException(handler, e);
489 public void execute(String command) {
490 execute(new StringReader(command), null);
493 public void execute(String command, SCLReportingHandler handler) {
494 execute(new StringReader(command), handler);
497 public CompilationError[] validate(String command) {
498 return CompilationError.EMPTY_ARRAY;
501 return CompilationError.EMPTY_ARRAY;
502 } catch(SCLExpressionCompilationException e) {
503 return e.getErrors();
507 private static final String THIS_CLASS_NAME = CommandSession.class.getName();
509 public static void formatException(
510 SCLReportingHandler handler,
512 formatException(handler, null, e);
515 private static void formatException(
516 SCLReportingHandler handler,
517 StackTraceElement[] enclosingTrace,
519 StackTraceElement[] elements = e.getStackTrace();
520 Throwable cause = e.getCause();
522 formatException(handler, elements, cause);
523 handler.printError("Rethrown as ");
525 handler.printError(e.toString());
526 int endPos = elements.length;
527 if(enclosingTrace != null) {
528 int p = enclosingTrace.length;
529 while(endPos > 0 && p > 0 && elements[endPos-1].equals(enclosingTrace[p-1])) {
535 for(int i=0;i<endPos;++i) {
536 StackTraceElement element = elements[i];
537 if(element.getMethodName().equals("execute") &&
538 element.getClassName().equals(THIS_CLASS_NAME)) {
541 element = elements[endPos-1];
542 String className = element.getClassName();
543 if(className.startsWith("org.simantics.scl.compiler.top.SCLExpressionCompiler")
544 //|| element.getClassName().startsWith("org.simantics.scl.compiler.interpreted.")
545 || className.startsWith("org.simantics.scl.runtime.function.FunctionImpl")
546 //|| className.startsWith("tempsclpackage")
556 for(int i=0;i<endPos;++i) {
557 StringBuilder b = new StringBuilder();
558 StackTraceElement element = elements[i];
559 String className = element.getClassName();
560 if(className.equals("org.simantics.scl.compiler.interpreted.IApply")
561 || className.equals("org.simantics.scl.compiler.interpreted.ILet")
562 || className.startsWith("tempsclpackage"))
564 if(className.startsWith("org.simantics.scl.compiler.interpreted.ILambda")) {
565 b.append("\tat command line\n");
568 String methodName = element.getMethodName();
569 if(className.startsWith("org.simantics.scl.runtime.function.FunctionImpl") &&
570 methodName.equals("applyArray"))
572 String fileName = element.getFileName();
573 if("_SCL_Closure".equals(fileName))
576 if("_SCL_Module".equals(fileName)
577 || "_SCL_TypeClassInstance".equals(fileName))
578 b.append(NameMangling.demangle(methodName))
579 .append('(').append(element.getLineNumber()).append(')');
582 handler.printError(b.toString());
586 public void setVariable(String name, Type type, Object value) {
587 variableValues.put(name, value);
588 variableTypes.put(name, type);
591 public Object getVariableValue(String name) {
592 return variableValues.get(name);
595 public Type getVariableType(String name) {
596 return variableTypes.get(name);
599 public void removeVariable(String name) {
600 variableValues.remove(name);
601 variableTypes.remove(name);
604 public void removeVariables() {
605 variableValues.clear();
606 variableTypes.clear();
609 public Set<String> getVariables() {
610 return variableTypes.keySet();
613 public ArrayList<CommandSessionImportEntry> getImportEntries() {
614 return importEntries;
617 public void setImportEntries(
618 ArrayList<CommandSessionImportEntry> importEntries) {
619 this.importEntries = importEntries;
620 updateRuntimeEnvironment(true);
623 public void runFromFile(String fileName, SCLReportingHandler handler) {
625 Reader reader = new LaxUTF8Reader(fileName);
627 execute(reader, handler);
631 } catch(IOException e) {
632 formatException(handler, e);