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;
81 * Only checks the commands for compilation errors but does not run them.
83 private boolean validateOnly;
85 public CommandSession(ModuleRepository moduleRepository, SCLReportingHandler handler) {
86 this.moduleRepository = moduleRepository;
87 this.defaultHandler = new PrintDecorator(
88 handler == null ? SCLReportingHandler.DEFAULT : handler);
89 updateRuntimeEnvironment(true);
92 private static EnvironmentSpecification createEnvironmentSpecification(Collection<CommandSessionImportEntry> importEntries) {
93 EnvironmentSpecification spec = new EnvironmentSpecification();
94 spec.importModule("Builtin", "");
95 spec.importModule("StandardLibrary", "");
96 spec.importModule("Expressions/Context", null);
97 for(CommandSessionImportEntry entry : importEntries)
98 if(!entry.disabled && !entry.hasError)
99 spec.importModule(entry.moduleName, entry.localName);
103 public void updateRuntimeEnvironment(boolean clearErrorsFlags) {
105 for(CommandSessionImportEntry entry : importEntries)
106 entry.hasError = false;
107 EnvironmentSpecification environmentSpecification = createEnvironmentSpecification(importEntries);
109 runtimeEnvironment = null;
112 runtimeEnvironment = moduleRepository.createRuntimeEnvironment(
113 environmentSpecification,
114 getClass().getClassLoader());
115 } catch(ImportFailureException e) {
116 THashSet<String> failedModules = new THashSet<String>();
117 for(ImportFailure failure : e.failures) {
118 failedModules.add(failure.moduleName);
119 defaultHandler.printError(failure.toString());
120 if(failure.reason instanceof CompilationError[])
121 for(CompilationError error : (CompilationError[])failure.reason) {
122 defaultHandler.printError(" " + error.description);
125 for(CommandSessionImportEntry entry : importEntries)
126 if(failedModules.contains(entry.moduleName))
127 entry.hasError = true;
128 environmentSpecification = createEnvironmentSpecification(importEntries);
130 runtimeEnvironment = moduleRepository.createRuntimeEnvironment(
131 environmentSpecification,
132 getClass().getClassLoader());
133 } catch (ImportFailureException e1) {
134 for(ImportFailure failure : e1.failures)
135 defaultHandler.printError(failure.toString());
138 } catch(RuntimeException e) {
142 valueToStringConverter = new ValueToStringConverter(runtimeEnvironment);
145 public RuntimeEnvironment getRuntimeEnvironment() {
146 return runtimeEnvironment;
149 public ModuleRepository getModuleRepository() {
150 return moduleRepository;
153 private static class CancelExecution extends RuntimeException {
154 private static final long serialVersionUID = -6925642906311538873L;
157 private LocalStorage localStorage = new LocalStorage() {
159 public void store(String name, Object value, Type type) {
160 variableValues.put(name, value);
161 variableTypes.put(name, type);
165 private static class LocalFunction {
166 final Function function;
169 public LocalFunction(Function function, Type type) {
170 this.function = function;
175 private static final THashMap<String, LocalFunction> LOCAL_FUNCTIONS = new THashMap<String, LocalFunction>();
177 LOCAL_FUNCTIONS.put("runFromFile", new LocalFunction(new FunctionImpl2<CommandSession, String, Tuple0>() {
179 public Tuple0 apply(final CommandSession commandSession, String fileName) {
180 SCLContext context = SCLContext.getCurrent();
181 commandSession.runFromFile(fileName, (SCLReportingHandler)context.get(SCLReportingHandler.REPORTING_HANDLER));
182 return Tuple0.INSTANCE;
184 }, Types.functionE(Types.STRING, Types.PROC, Types.UNIT)));
185 LOCAL_FUNCTIONS.put("runTest", new LocalFunction(new FunctionImpl2<CommandSession, String, Tuple0>() {
187 public Tuple0 apply(final CommandSession commandSession, String fileName) {
188 SCLContext context = SCLContext.getCurrent();
189 SCLReportingHandler handler = (SCLReportingHandler)context.get(SCLReportingHandler.REPORTING_HANDLER);
191 BufferedReader reader = new BufferedReader(new LaxUTF8Reader(fileName));
193 new TestScriptExecutor(commandSession, reader, handler).execute();
197 } catch(IOException e) {
198 handler.printError(e.getMessage());
200 return Tuple0.INSTANCE;
202 }, Types.functionE(Types.STRING, Types.PROC, Types.UNIT)));
203 LOCAL_FUNCTIONS.put("reset", new LocalFunction(new FunctionImpl2<CommandSession, Tuple0, Tuple0>() {
205 public Tuple0 apply(CommandSession commandSession, Tuple0 dummy) {
206 commandSession.removeTransientImports();
207 commandSession.removeVariables();
208 commandSession.moduleRepository.getSourceRepository().checkUpdates();
209 commandSession.updateRuntimeEnvironment(true);
210 return Tuple0.INSTANCE;
212 }, Types.functionE(Types.UNIT, Types.PROC, Types.UNIT)));
213 LOCAL_FUNCTIONS.put("variables", new LocalFunction(new FunctionImpl2<CommandSession, Tuple0, List<String>>() {
215 public List<String> apply(CommandSession commandSession, Tuple0 dummy) {
216 ArrayList<String> result = new ArrayList<String>(commandSession.variableTypes.keySet());
217 Collections.sort(result);
220 }, Types.functionE(Types.PUNIT, Types.PROC, Types.list(Types.STRING))));
221 LOCAL_FUNCTIONS.put("startPrintingToFile", new LocalFunction(new FunctionImpl2<CommandSession, String, Tuple0>() {
223 public Tuple0 apply(final CommandSession commandSession, String fileName) {
225 if(commandSession.fileOutput != null) {
226 commandSession.fileOutput.close();
227 SCLReporting.printError("Printing to file was already enabled. Stopped the previous printing.");
229 commandSession.fileOutput = new PrintStream(fileName, "UTF-8");
230 } catch (FileNotFoundException e) {
231 throw new RuntimeException(e);
232 } catch (UnsupportedEncodingException e) {
233 throw new RuntimeException(e);
235 return Tuple0.INSTANCE;
237 }, Types.functionE(Types.STRING, Types.PROC, Types.UNIT)));
238 LOCAL_FUNCTIONS.put("startAppendingToFile", new LocalFunction(new FunctionImpl2<CommandSession, String, Tuple0>() {
240 public Tuple0 apply(final CommandSession commandSession, String fileName) {
242 if(commandSession.fileOutput != null) {
243 commandSession.fileOutput.close();
244 SCLReporting.printError("Printing to file was already enabled. Stopped the previous printing.");
246 FileOutputStream stream = new FileOutputStream(fileName, true);
247 commandSession.fileOutput = new PrintStream(stream, false, "UTF-8");
248 } catch (FileNotFoundException e) {
249 throw new RuntimeException(e);
250 } catch (UnsupportedEncodingException e) {
251 throw new RuntimeException(e);
253 return Tuple0.INSTANCE;
255 }, Types.functionE(Types.STRING, Types.PROC, Types.UNIT)));
256 LOCAL_FUNCTIONS.put("stopPrintingToFile", new LocalFunction(new FunctionImpl2<CommandSession, Tuple0, Tuple0>() {
258 public Tuple0 apply(final CommandSession commandSession, Tuple0 dummy) {
259 if(commandSession.fileOutput != null) {
260 commandSession.fileOutput.close();
261 commandSession.fileOutput = null;
263 return Tuple0.INSTANCE;
265 }, Types.functionE(Types.PUNIT, Types.PROC, Types.UNIT)));
268 private LocalEnvironment createLocalEnvironment() {
269 return new AbstractLocalEnvironment() {
270 Variable contextVariable = new Variable("context", Names.Expressions_Context_Context);
272 public Expression resolve(Environment environment, String localName) {
273 Type type = variableTypes.get(localName);
276 new EConstant(environment.getValue(Names.Expressions_Context_contextGet), type),
277 new EVariable(contextVariable),
278 new ELiteral(new StringConstant(localName))
280 LocalFunction localFunction = LOCAL_FUNCTIONS.get(localName);
281 if(localFunction != null) {
282 return new EExternalConstant(
283 localFunction.function.apply(CommandSession.this),
289 protected Variable[] getContextVariables() {
290 return new Variable[] { contextVariable };
293 public void forNames(TObjectProcedure<String> proc) {
294 for(String name : variableTypes.keySet())
296 for(String name : LOCAL_FUNCTIONS.keySet())
302 protected void removeTransientImports() {
303 ArrayList<CommandSessionImportEntry> newEntries = new ArrayList<CommandSessionImportEntry>(importEntries.size());
304 for(CommandSessionImportEntry entry : importEntries)
306 newEntries.add(entry);
307 importEntries = newEntries;
310 public THashMap<String,Type> localNamesForContentProposals() {
311 THashMap<String,Type> result = new THashMap<String,Type>();
312 for(Map.Entry<String,LocalFunction> entry : LOCAL_FUNCTIONS.entrySet())
313 result.put(entry.getKey(), entry.getValue().type);
314 result.putAll(variableTypes);
318 private CompiledCommand compile(Expression expression) throws SCLExpressionCompilationException {
319 LocalEnvironment localEnvironment = createLocalEnvironment();
320 if(runtimeEnvironment == null)
321 throw new SCLExpressionCompilationException(new CompilationError[] {
322 new CompilationError("Compilation failed: imports in the current environment have failed.")
324 ExpressionEvaluator evaluator = new ExpressionEvaluator(runtimeEnvironment, localStorage, expression);
325 Function command = (Function)evaluator
326 .localEnvironment(localEnvironment)
327 .decorateExpression(true)
328 .validateOnly(validateOnly)
330 return new CompiledCommand(command, evaluator.getType());
333 class PrintDecorator extends DelegatingSCLReportingHandler {
334 public PrintDecorator(SCLReportingHandler baseHandler) {
339 public void print(String text) {
341 if(fileOutput != null)
342 fileOutput.println(text);
346 public void printCommand(String command) {
347 super.printCommand(command);
348 if(fileOutput != null)
349 fileOutput.println("> " + command);
353 public void printError(String error) {
354 super.printError(error);
355 if(fileOutput != null)
356 fileOutput.println(error);
360 @SuppressWarnings("unchecked")
361 private void execute(MemoReader reader, Expression expression, final SCLReportingHandler handler) {
362 SCLContext context = SCLContext.getCurrent();
363 Object oldPrinter = context.put(SCLReportingHandler.REPORTING_HANDLER, handler);
365 CompiledCommand command;
367 handler.printCommand(reader.extractString(expression.location));
368 command = compile(expression);
369 } catch (SCLExpressionCompilationException e) {
372 CompilationError[] errors = e.getErrors();
373 for(CompilationError error : errors) {
374 if(error.location != Locations.NO_LOCATION)
375 handler.printError(reader.locationUnderlining(error.location));
376 handler.printError(error.description);
378 throw new CancelExecution();
380 reader.forgetEverythingBefore(Locations.endOf(expression.location));
383 Object resultValue = command.command.apply(variableValues);
384 String resultString = toString(resultValue, command.type);
385 if(!resultString.isEmpty())
386 handler.print(resultString);
388 } catch(Exception e) {
391 if(!(e instanceof CancelExecution)) {
392 if(e instanceof InterruptedException)
393 handler.printError("Execution interrupted.");
395 formatException(handler, e);
397 throw new CancelExecution();
399 context.put(SCLReportingHandler.REPORTING_HANDLER, oldPrinter);
403 private String toString(Object value, Type type) {
404 if(type.equals(Types.UNIT))
407 return valueToStringConverter.show(value, type);
408 } catch (SCLExpressionCompilationException e) {
409 return "<value of type " + type + ">";
413 class CommandParser extends SCLParserImpl {
414 SCLReportingHandler handler;
416 public CommandParser(SCLReportingHandler handler, MemoReader reader) {
418 this.reader = reader;
419 this.handler = handler;
424 if(currentBlock != null) {
426 LinkedList<Statement> statements = currentBlock.getStatements();
427 currentBlock.location = Locations.combine(
428 statements.getFirst().location,
429 statements.getLast().location);
430 execute(reader, currentBlock, handler);
435 protected Object reduceStatementCommand() {
436 Statement statement = (Statement)get(0);
437 if(statement.mayBeRecursive()) {
438 if(currentBlock == null)
439 currentBlock = new EBlock();
440 currentBlock.addStatement(statement);
445 if(statement instanceof GuardStatement)
446 execute(reader, ((GuardStatement)statement).value, handler);
448 EBlock block = new EBlock();
449 block.addStatement(statement);
450 block.location = statement.location;
451 execute(reader, block, handler);
458 protected Object reduceImportCommand() {
462 ImportDeclaration importDeclaration = (ImportDeclaration)get(0);
463 handler.printCommand(reader.extractString(importDeclaration.location));
464 new CommandSessionImportEntry(importDeclaration.moduleName,
465 importDeclaration.localName).addTo(importEntries);
466 updateRuntimeEnvironment(false);
471 private void checkInterrupted() {
472 if(Thread.interrupted()) {
473 defaultHandler.printError("Execution interrupted.");
474 throw new CancelExecution();
478 private CompilationError[] validate(Reader commandReader) {
479 CommandParser parser = new CommandParser(defaultHandler, new MemoReader(commandReader));
482 parser.parseCommands();
483 parser.finishBlock();
484 return CompilationError.EMPTY_ARRAY;
485 } catch(SCLExpressionCompilationException e) {
486 return e.getErrors();
487 } catch(SCLSyntaxErrorException e) {
488 return new CompilationError[] { new CompilationError(e.location, e.getMessage()) };
489 } catch(Exception e) {
490 return new CompilationError[] { new CompilationError(Locations.NO_LOCATION, e.getMessage()) };
492 validateOnly = false;
496 public void execute(Reader commandReader, SCLReportingHandler handler) {
498 handler = defaultHandler;
499 else if (!(handler instanceof PrintDecorator))
500 handler = new PrintDecorator(handler);
501 CommandParser parser = new CommandParser(handler, new MemoReader(commandReader));
503 parser.parseCommands();
504 parser.finishBlock();
505 } catch(CancelExecution e) {
506 } catch(SCLSyntaxErrorException e) {
507 handler.printCommand(parser.reader.getLastCommand());
508 if(e.location != Locations.NO_LOCATION)
509 handler.printError(parser.reader.locationUnderlining(e.location));
510 handler.printError(e.getMessage());
511 } catch(Exception e) {
512 if(e instanceof InterruptedException)
513 handler.printError("Execution interrupted.");
515 formatException(handler, e);
519 public void execute(String command) {
520 execute(new StringReader(command), null);
523 public void execute(String command, SCLReportingHandler handler) {
524 execute(new StringReader(command), handler);
527 private static final String THIS_CLASS_NAME = CommandSession.class.getName();
529 public static void formatException(
530 SCLReportingHandler handler,
532 formatException(handler, null, e);
535 private static void formatException(
536 SCLReportingHandler handler,
537 StackTraceElement[] enclosingTrace,
539 StackTraceElement[] elements = e.getStackTrace();
540 Throwable cause = e.getCause();
542 formatException(handler, elements, cause);
543 handler.printError("Rethrown as ");
545 handler.printError(e.toString());
546 int endPos = elements.length;
547 if(enclosingTrace != null) {
548 int p = enclosingTrace.length;
549 while(endPos > 0 && p > 0 && elements[endPos-1].equals(enclosingTrace[p-1])) {
555 for(int i=0;i<endPos;++i) {
556 StackTraceElement element = elements[i];
557 if(element.getMethodName().equals("execute") &&
558 element.getClassName().equals(THIS_CLASS_NAME)) {
561 element = elements[endPos-1];
562 String className = element.getClassName();
563 if(className.startsWith("org.simantics.scl.compiler.top.SCLExpressionCompiler")
564 //|| element.getClassName().startsWith("org.simantics.scl.compiler.interpreted.")
565 || className.startsWith("org.simantics.scl.runtime.function.FunctionImpl")
566 //|| className.startsWith("tempsclpackage")
576 for(int i=0;i<endPos;++i) {
577 StringBuilder b = new StringBuilder();
578 StackTraceElement element = elements[i];
579 String className = element.getClassName();
580 if(className.equals("org.simantics.scl.compiler.interpreted.IApply")
581 || className.equals("org.simantics.scl.compiler.interpreted.ILet")
582 || className.startsWith("tempsclpackage"))
584 if(className.startsWith("org.simantics.scl.compiler.interpreted.ILambda")) {
585 b.append("\tat command line\n");
588 String methodName = element.getMethodName();
589 if(className.startsWith("org.simantics.scl.runtime.function.FunctionImpl") &&
590 methodName.equals("applyArray"))
592 String fileName = element.getFileName();
593 if("_SCL_Closure".equals(fileName))
596 if("_SCL_Module".equals(fileName)
597 || "_SCL_TypeClassInstance".equals(fileName))
598 b.append(NameMangling.demangle(methodName))
599 .append('(').append(element.getLineNumber()).append(')');
602 handler.printError(b.toString());
606 public void setVariable(String name, Type type, Object value) {
607 variableValues.put(name, value);
608 variableTypes.put(name, type);
611 public Object getVariableValue(String name) {
612 return variableValues.get(name);
615 public Type getVariableType(String name) {
616 return variableTypes.get(name);
619 public void removeVariable(String name) {
620 variableValues.remove(name);
621 variableTypes.remove(name);
624 public void removeVariables() {
625 variableValues.clear();
626 variableTypes.clear();
629 public Set<String> getVariables() {
630 return variableTypes.keySet();
633 public ArrayList<CommandSessionImportEntry> getImportEntries() {
634 return importEntries;
637 public void setImportEntries(
638 ArrayList<CommandSessionImportEntry> importEntries) {
639 this.importEntries = importEntries;
640 updateRuntimeEnvironment(true);
643 public void runFromFile(String fileName, SCLReportingHandler handler) {
645 Reader reader = new LaxUTF8Reader(fileName);
647 execute(reader, handler);
651 } catch(IOException e) {
652 formatException(handler, e);
656 public static CompilationError[] validate(ModuleRepository moduleRepository,StringReader commandReader) {
657 CommandSession session = new CommandSession(moduleRepository, null);
658 return session.validate(commandReader);
661 public static CompilationError[] validate(ModuleRepository moduleRepository,String command) {
662 return validate(moduleRepository, new StringReader(command));
665 public CompilationError[] validate(String command) {
666 return validate(new StringReader(command));