X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.scl.compiler%2Fsrc%2Forg%2Fsimantics%2Fscl%2Fcompiler%2Fcommands%2FCommandSession.java;fp=bundles%2Forg.simantics.scl.compiler%2Fsrc%2Forg%2Fsimantics%2Fscl%2Fcompiler%2Fcommands%2FCommandSession.java;h=8ad47b0a093d9b6ebeecc121e603cf4e0ffaa6f7;hp=0000000000000000000000000000000000000000;hb=969bd23cab98a79ca9101af33334000879fb60c5;hpb=866dba5cd5a3929bbeae85991796acb212338a08 diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/commands/CommandSession.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/commands/CommandSession.java new file mode 100644 index 000000000..8ad47b0a0 --- /dev/null +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/commands/CommandSession.java @@ -0,0 +1,640 @@ +package org.simantics.scl.compiler.commands; + +import gnu.trove.map.hash.THashMap; +import gnu.trove.procedure.TObjectProcedure; +import gnu.trove.set.hash.THashSet; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.simantics.scl.compiler.common.names.Name; +import org.simantics.scl.compiler.constants.StringConstant; +import org.simantics.scl.compiler.elaboration.expressions.EApply; +import org.simantics.scl.compiler.elaboration.expressions.EBlock; +import org.simantics.scl.compiler.elaboration.expressions.EConstant; +import org.simantics.scl.compiler.elaboration.expressions.EExternalConstant; +import org.simantics.scl.compiler.elaboration.expressions.ELiteral; +import org.simantics.scl.compiler.elaboration.expressions.EVariable; +import org.simantics.scl.compiler.elaboration.expressions.Expression; +import org.simantics.scl.compiler.elaboration.expressions.Variable; +import org.simantics.scl.compiler.elaboration.expressions.block.GuardStatement; +import org.simantics.scl.compiler.elaboration.expressions.block.Statement; +import org.simantics.scl.compiler.environment.AbstractLocalEnvironment; +import org.simantics.scl.compiler.environment.Environment; +import org.simantics.scl.compiler.environment.LocalEnvironment; +import org.simantics.scl.compiler.environment.specification.EnvironmentSpecification; +import org.simantics.scl.compiler.errors.CompilationError; +import org.simantics.scl.compiler.errors.Locations; +import org.simantics.scl.compiler.internal.codegen.utils.NameMangling; +import org.simantics.scl.compiler.internal.parsing.exceptions.SCLSyntaxErrorException; +import org.simantics.scl.compiler.internal.parsing.parser.SCLParserImpl; +import org.simantics.scl.compiler.internal.parsing.utils.LaxUTF8Reader; +import org.simantics.scl.compiler.internal.parsing.utils.MemoReader; +import org.simantics.scl.compiler.module.ImportDeclaration; +import org.simantics.scl.compiler.module.repository.ImportFailure; +import org.simantics.scl.compiler.module.repository.ImportFailureException; +import org.simantics.scl.compiler.module.repository.ModuleRepository; +import org.simantics.scl.compiler.runtime.RuntimeEnvironment; +import org.simantics.scl.compiler.top.ExpressionEvaluator; +import org.simantics.scl.compiler.top.LocalStorage; +import org.simantics.scl.compiler.top.SCLExpressionCompilationException; +import org.simantics.scl.compiler.types.TCon; +import org.simantics.scl.compiler.types.Type; +import org.simantics.scl.compiler.types.Types; +import org.simantics.scl.runtime.SCLContext; +import org.simantics.scl.runtime.function.Function; +import org.simantics.scl.runtime.function.FunctionImpl2; +import org.simantics.scl.runtime.reporting.DelegatingSCLReportingHandler; +import org.simantics.scl.runtime.reporting.SCLReporting; +import org.simantics.scl.runtime.reporting.SCLReportingHandler; +import org.simantics.scl.runtime.tuple.Tuple0; + + +public class CommandSession { + + ModuleRepository moduleRepository; + SCLReportingHandler defaultHandler; + + RuntimeEnvironment runtimeEnvironment; + ValueToStringConverter valueToStringConverter; + + ArrayList importEntries = new ArrayList(); + + THashMap variableValues = new THashMap(); + THashMap variableTypes = new THashMap(); + + PrintStream fileOutput; + + private static final String CONTEXT_MODULE = "Expressions/Context"; + private static final TCon CONTEXT_TYPE = Types.con(CONTEXT_MODULE, "Context"); + private static final Name CONTEXT_GET = Name.create(CONTEXT_MODULE, "contextGet"); + + public CommandSession(ModuleRepository moduleRepository, SCLReportingHandler handler) { + this.moduleRepository = moduleRepository; + this.defaultHandler = new PrintDecorator( + handler == null ? SCLReportingHandler.DEFAULT : handler); + updateRuntimeEnvironment(true); + } + + private static EnvironmentSpecification createEnvironmentSpecification(Collection importEntries) { + EnvironmentSpecification spec = new EnvironmentSpecification(); + spec.importModule("Builtin", ""); + spec.importModule("StandardLibrary", ""); + spec.importModule("Expressions/Context", null); + for(CommandSessionImportEntry entry : importEntries) + if(!entry.disabled && !entry.hasError) + spec.importModule(entry.moduleName, entry.localName); + return spec; + } + + public void updateRuntimeEnvironment(boolean clearErrorsFlags) { + if(clearErrorsFlags) + for(CommandSessionImportEntry entry : importEntries) + entry.hasError = false; + EnvironmentSpecification environmentSpecification = createEnvironmentSpecification(importEntries); + + runtimeEnvironment = null; + try { + try { + runtimeEnvironment = moduleRepository.createRuntimeEnvironment( + environmentSpecification, + getClass().getClassLoader()); + } catch(ImportFailureException e) { + THashSet failedModules = new THashSet(); + for(ImportFailure failure : e.failures) { + failedModules.add(failure.moduleName); + defaultHandler.printError(failure.toString()); + if(failure.reason instanceof CompilationError[]) + for(CompilationError error : (CompilationError[])failure.reason) { + defaultHandler.printError(" " + error.description); + } + } + for(CommandSessionImportEntry entry : importEntries) + if(failedModules.contains(entry.moduleName)) + entry.hasError = true; + environmentSpecification = createEnvironmentSpecification(importEntries); + try { + runtimeEnvironment = moduleRepository.createRuntimeEnvironment( + environmentSpecification, + getClass().getClassLoader()); + } catch (ImportFailureException e1) { + for(ImportFailure failure : e1.failures) + defaultHandler.printError(failure.toString()); + } + } + } catch(RuntimeException e) { + e.printStackTrace(); + throw e; + } + valueToStringConverter = new ValueToStringConverter(runtimeEnvironment); + } + + public RuntimeEnvironment getRuntimeEnvironment() { + return runtimeEnvironment; + } + + public ModuleRepository getModuleRepository() { + return moduleRepository; + } + + private static class CancelExecution extends RuntimeException { + private static final long serialVersionUID = -6925642906311538873L; + } + + private LocalStorage localStorage = new LocalStorage() { + @Override + public void store(String name, Object value, Type type) { + variableValues.put(name, value); + variableTypes.put(name, type); + } + }; + + private static class LocalFunction { + final Function function; + final Type type; + + public LocalFunction(Function function, Type type) { + this.function = function; + this.type = type; + } + } + + private static final THashMap LOCAL_FUNCTIONS = new THashMap(); + static { + LOCAL_FUNCTIONS.put("runFromFile", new LocalFunction(new FunctionImpl2() { + @Override + public Tuple0 apply(final CommandSession commandSession, String fileName) { + SCLContext context = SCLContext.getCurrent(); + commandSession.runFromFile(fileName, (SCLReportingHandler)context.get(SCLReportingHandler.REPORTING_HANDLER)); + return Tuple0.INSTANCE; + } + }, Types.functionE(Types.STRING, Types.PROC, Types.UNIT))); + LOCAL_FUNCTIONS.put("runTest", new LocalFunction(new FunctionImpl2() { + @Override + public Tuple0 apply(final CommandSession commandSession, String fileName) { + SCLContext context = SCLContext.getCurrent(); + SCLReportingHandler handler = (SCLReportingHandler)context.get(SCLReportingHandler.REPORTING_HANDLER); + try { + BufferedReader reader = new BufferedReader(new LaxUTF8Reader(fileName)); + try { + new TestScriptExecutor(commandSession, reader, handler).execute(); + } finally { + reader.close(); + } + } catch(IOException e) { + handler.printError(e.getMessage()); + } + return Tuple0.INSTANCE; + } + }, Types.functionE(Types.STRING, Types.PROC, Types.UNIT))); + LOCAL_FUNCTIONS.put("reset", new LocalFunction(new FunctionImpl2() { + @Override + public Tuple0 apply(CommandSession commandSession, Tuple0 _) { + commandSession.removeTransientImports(); + commandSession.removeVariables(); + commandSession.moduleRepository.getSourceRepository().checkUpdates(); + commandSession.updateRuntimeEnvironment(true); + return Tuple0.INSTANCE; + } + }, Types.functionE(Types.UNIT, Types.PROC, Types.UNIT))); + LOCAL_FUNCTIONS.put("variables", new LocalFunction(new FunctionImpl2>() { + @Override + public List apply(CommandSession commandSession, Tuple0 _) { + ArrayList result = new ArrayList(commandSession.variableTypes.keySet()); + Collections.sort(result); + return result; + } + }, Types.functionE(Types.PUNIT, Types.PROC, Types.list(Types.STRING)))); + LOCAL_FUNCTIONS.put("startPrintingToFile", new LocalFunction(new FunctionImpl2() { + @Override + public Tuple0 apply(final CommandSession commandSession, String fileName) { + try { + if(commandSession.fileOutput != null) { + commandSession.fileOutput.close(); + SCLReporting.printError("Printing to file was already enabled. Stopped the previous printing."); + } + commandSession.fileOutput = new PrintStream(fileName, "UTF-8"); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + return Tuple0.INSTANCE; + } + }, Types.functionE(Types.STRING, Types.PROC, Types.UNIT))); + LOCAL_FUNCTIONS.put("startAppendingToFile", new LocalFunction(new FunctionImpl2() { + @Override + public Tuple0 apply(final CommandSession commandSession, String fileName) { + try { + if(commandSession.fileOutput != null) { + commandSession.fileOutput.close(); + SCLReporting.printError("Printing to file was already enabled. Stopped the previous printing."); + } + FileOutputStream stream = new FileOutputStream(fileName, true); + commandSession.fileOutput = new PrintStream(stream, false, "UTF-8"); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + return Tuple0.INSTANCE; + } + }, Types.functionE(Types.STRING, Types.PROC, Types.UNIT))); + LOCAL_FUNCTIONS.put("stopPrintingToFile", new LocalFunction(new FunctionImpl2() { + @Override + public Tuple0 apply(final CommandSession commandSession, Tuple0 _) { + if(commandSession.fileOutput != null) { + commandSession.fileOutput.close(); + commandSession.fileOutput = null; + } + return Tuple0.INSTANCE; + } + }, Types.functionE(Types.PUNIT, Types.PROC, Types.UNIT))); + } + + private LocalEnvironment createLocalEnvironment() { + return new AbstractLocalEnvironment() { + Variable contextVariable = new Variable("context", CONTEXT_TYPE); + @Override + public Expression resolve(Environment environment, String localName) { + Type type = variableTypes.get(localName); + if(type != null) + return new EApply( + new EConstant(environment.getValue(CONTEXT_GET), type), + new EVariable(contextVariable), + new ELiteral(new StringConstant(localName)) + ); + LocalFunction localFunction = LOCAL_FUNCTIONS.get(localName); + if(localFunction != null) { + return new EExternalConstant( + localFunction.function.apply(CommandSession.this), + localFunction.type); + } + return null; + } + @Override + protected Variable[] getContextVariables() { + return new Variable[] { contextVariable }; + } + @Override + public void forNames(TObjectProcedure proc) { + for(String name : variableTypes.keySet()) + proc.execute(name); + for(String name : LOCAL_FUNCTIONS.keySet()) + proc.execute(name); + } + }; + } + + protected void removeTransientImports() { + ArrayList newEntries = new ArrayList(importEntries.size()); + for(CommandSessionImportEntry entry : importEntries) + if(entry.persistent) + newEntries.add(entry); + importEntries = newEntries; + } + + public THashMap localNamesForContentProposals() { + THashMap result = new THashMap(); + for(Map.Entry entry : LOCAL_FUNCTIONS.entrySet()) + result.put(entry.getKey(), entry.getValue().type); + result.putAll(variableTypes); + return result; + } + + private CompiledCommand compile(Expression expression) throws SCLExpressionCompilationException { + LocalEnvironment localEnvironment = createLocalEnvironment(); + if(runtimeEnvironment == null) + throw new SCLExpressionCompilationException(new CompilationError[] { + new CompilationError("Compilation failed: imports in the current environment have failed.") + }); + ExpressionEvaluator evaluator = new ExpressionEvaluator(runtimeEnvironment, localStorage, expression); + Function command = (Function)evaluator + .localEnvironment(localEnvironment) + .decorateExpression(true) + .eval(); + return new CompiledCommand(command, evaluator.getType()); + } + + class PrintDecorator extends DelegatingSCLReportingHandler { + public PrintDecorator(SCLReportingHandler baseHandler) { + super(baseHandler); + } + + @Override + public void print(String text) { + super.print(text); + if(fileOutput != null) + fileOutput.println(text); + } + + @Override + public void printCommand(String command) { + super.printCommand(command); + if(fileOutput != null) + fileOutput.println("> " + command); + } + + @Override + public void printError(String error) { + super.printError(error); + if(fileOutput != null) + fileOutput.println(error); + } + } + + @SuppressWarnings("unchecked") + private void execute(MemoReader reader, Expression expression, final SCLReportingHandler handler) { + SCLContext context = SCLContext.getCurrent(); + Object oldPrinter = context.put(SCLReportingHandler.REPORTING_HANDLER, handler); + try { + CompiledCommand command; + try { + handler.printCommand(reader.extractString(expression.location)); + command = compile(expression); + } catch (SCLExpressionCompilationException e) { + CompilationError[] errors = ((SCLExpressionCompilationException)e).getErrors(); + for(CompilationError error : errors) { + if(error.location != Locations.NO_LOCATION) + handler.printError(reader.locationUnderlining(error.location)); + handler.printError(error.description); + } + throw new CancelExecution(); + } + reader.forgetEverythingBefore(Locations.endOf(expression.location)); + + Object resultValue = command.command.apply(variableValues); + String resultString = toString(resultValue, command.type); + if(!resultString.isEmpty()) + handler.print(resultString); + } catch(Exception e) { + if(!(e instanceof CancelExecution)) { + if(e instanceof InterruptedException) + handler.printError("Execution interrupted."); + else + formatException(handler, e); + } + throw new CancelExecution(); + } finally { + context.put(SCLReportingHandler.REPORTING_HANDLER, oldPrinter); + } + } + + private String toString(Object value, Type type) { + if(type.equals(Types.UNIT)) + return ""; + try { + return valueToStringConverter.show(value, type); + } catch (SCLExpressionCompilationException e) { + return ""; + } + } + + class CommandParser extends SCLParserImpl { + SCLReportingHandler handler; + MemoReader reader; + public CommandParser(SCLReportingHandler handler, MemoReader reader) { + super(reader); + this.reader = reader; + this.handler = handler; + } + + EBlock currentBlock; + void finishBlock() { + if(currentBlock != null) { + checkInterrupted(); + LinkedList statements = currentBlock.getStatements(); + currentBlock.location = Locations.combine( + statements.getFirst().location, + statements.getLast().location); + execute(reader, currentBlock, handler); + currentBlock = null; + } + } + @Override + protected Object reduceStatementCommand() { + Statement statement = (Statement)get(0); + if(statement.mayBeRecursive()) { + if(currentBlock == null) + currentBlock = new EBlock(); + currentBlock.addStatement(statement); + } + else { + finishBlock(); + checkInterrupted(); + if(statement instanceof GuardStatement) + execute(reader, ((GuardStatement)statement).value, handler); + else { + EBlock block = new EBlock(); + block.addStatement(statement); + block.location = statement.location; + execute(reader, block, handler); + } + } + return null; + } + + @Override + protected Object reduceImportCommand() { + finishBlock(); + checkInterrupted(); + + ImportDeclaration importDeclaration = (ImportDeclaration)get(0); + handler.printCommand(reader.extractString(importDeclaration.location)); + new CommandSessionImportEntry(importDeclaration.moduleName, + importDeclaration.localName).addTo(importEntries); + updateRuntimeEnvironment(false); + return null; + } + } + + private void checkInterrupted() { + if(Thread.interrupted()) { + defaultHandler.printError("Execution interrupted."); + throw new CancelExecution(); + } + } + + public void execute(Reader commandReader, SCLReportingHandler handler) { + if(handler == null) + handler = defaultHandler; + else if (!(handler instanceof PrintDecorator)) + handler = new PrintDecorator(handler); + CommandParser parser = new CommandParser(handler, new MemoReader(commandReader)); + try { + parser.parseCommands(); + parser.finishBlock(); + } catch(CancelExecution e) { + } catch(SCLSyntaxErrorException e) { + handler.printCommand(parser.reader.getLastCommand()); + if(e.location != Locations.NO_LOCATION) + handler.printError(parser.reader.locationUnderlining(e.location)); + handler.printError(e.getMessage()); + } catch(Exception e) { + if(e instanceof InterruptedException) + handler.printError("Execution interrupted."); + else + formatException(handler, e); + } + } + + public void execute(String command) { + execute(new StringReader(command), null); + } + + public void execute(String command, SCLReportingHandler handler) { + execute(new StringReader(command), handler); + } + + public CompilationError[] validate(String command) { + return CompilationError.EMPTY_ARRAY; + /*try { + compile(command); + return CompilationError.EMPTY_ARRAY; + } catch(SCLExpressionCompilationException e) { + return e.getErrors(); + }*/ + } + + private static final String THIS_CLASS_NAME = CommandSession.class.getName(); + + public static void formatException( + SCLReportingHandler handler, + Throwable e) { + formatException(handler, null, e); + } + + private static void formatException( + SCLReportingHandler handler, + StackTraceElement[] enclosingTrace, + Throwable e) { + StackTraceElement[] elements = e.getStackTrace(); + Throwable cause = e.getCause(); + if(cause != null) { + formatException(handler, elements, cause); + handler.printError("Rethrown as "); + } + handler.printError(e.toString()); + int endPos = elements.length; + if(enclosingTrace != null) { + int p = enclosingTrace.length; + while(endPos > 0 && p > 0 && elements[endPos-1].equals(enclosingTrace[p-1])) { + --p; + --endPos; + } + } + else { + for(int i=0;i 0) { + element = elements[endPos-1]; + String className = element.getClassName(); + if(className.startsWith("org.simantics.scl.compiler.top.SCLExpressionCompiler") + //|| element.getClassName().startsWith("org.simantics.scl.compiler.interpreted.") + || className.startsWith("org.simantics.scl.runtime.function.FunctionImpl") + //|| className.startsWith("tempsclpackage") + ) + --endPos; + else + break; + } + break; + } + } + } + for(int i=0;i getVariables() { + return variableTypes.keySet(); + } + + public ArrayList getImportEntries() { + return importEntries; + } + + public void setImportEntries( + ArrayList importEntries) { + this.importEntries = importEntries; + updateRuntimeEnvironment(true); + } + + public void runFromFile(String fileName, SCLReportingHandler handler) { + try { + Reader reader = new LaxUTF8Reader(fileName); + try { + execute(reader, handler); + } finally { + reader.close(); + } + } catch(IOException e) { + formatException(handler, e); + } + } +}