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%2Ftop%2FExpressionEvaluator.java;fp=bundles%2Forg.simantics.scl.compiler%2Fsrc%2Forg%2Fsimantics%2Fscl%2Fcompiler%2Ftop%2FExpressionEvaluator.java;h=6cb68b67364046470a52ebcc7b3c6486bfaf9480;hp=0000000000000000000000000000000000000000;hb=969bd23cab98a79ca9101af33334000879fb60c5;hpb=866dba5cd5a3929bbeae85991796acb212338a08 diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/top/ExpressionEvaluator.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/top/ExpressionEvaluator.java new file mode 100644 index 000000000..6cb68b673 --- /dev/null +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/top/ExpressionEvaluator.java @@ -0,0 +1,436 @@ +package org.simantics.scl.compiler.top; + +import gnu.trove.set.hash.THashSet; + +import java.io.StringReader; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.ListIterator; +import java.util.Map; + +import org.simantics.scl.compiler.common.names.Name; +import org.simantics.scl.compiler.compilation.CodeGeneration; +import org.simantics.scl.compiler.constants.JavaStaticMethod; +import org.simantics.scl.compiler.constants.SCLConstant; +import org.simantics.scl.compiler.elaboration.contexts.SimplificationContext; +import org.simantics.scl.compiler.elaboration.contexts.TranslationContext; +import org.simantics.scl.compiler.elaboration.contexts.TypingContext; +import org.simantics.scl.compiler.elaboration.errors.NotPatternException; +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.EVar; +import org.simantics.scl.compiler.elaboration.expressions.Expression; +import org.simantics.scl.compiler.elaboration.expressions.block.GuardStatement; +import org.simantics.scl.compiler.elaboration.expressions.block.LetStatement; +import org.simantics.scl.compiler.elaboration.expressions.block.Statement; +import org.simantics.scl.compiler.elaboration.java.Builtins; +import org.simantics.scl.compiler.environment.Environment; +import org.simantics.scl.compiler.environment.LocalEnvironment; +import org.simantics.scl.compiler.errors.ErrorLog; +import org.simantics.scl.compiler.internal.codegen.references.IVal; +import org.simantics.scl.compiler.internal.codegen.ssa.SSAModule; +import org.simantics.scl.compiler.internal.codegen.types.DummyJavaReferenceValidator; +import org.simantics.scl.compiler.internal.codegen.types.JavaTypeTranslator; +import org.simantics.scl.compiler.internal.codegen.utils.CodeBuildingException; +import org.simantics.scl.compiler.internal.codegen.utils.JavaNamingPolicy; +import org.simantics.scl.compiler.internal.codegen.utils.ModuleBuilder; +import org.simantics.scl.compiler.internal.codegen.utils.TransientClassBuilder; +import org.simantics.scl.compiler.internal.codegen.utils.ValueFromMethod; +import org.simantics.scl.compiler.internal.codegen.writer.CodeWriter; +import org.simantics.scl.compiler.internal.codegen.writer.ExternalConstant; +import org.simantics.scl.compiler.internal.codegen.writer.ModuleWriter; +import org.simantics.scl.compiler.internal.elaboration.decomposed.DecomposedExpression; +import org.simantics.scl.compiler.internal.elaboration.utils.ExpressionDecorator; +import org.simantics.scl.compiler.internal.interpreted.IExpression; +import org.simantics.scl.compiler.internal.parsing.exceptions.SCLSyntaxErrorException; +import org.simantics.scl.compiler.internal.parsing.parser.SCLBlockParser; +import org.simantics.scl.compiler.internal.parsing.parser.SCLParserImpl; +import org.simantics.scl.compiler.internal.parsing.parser.SCLParserOptions; +import org.simantics.scl.compiler.runtime.MutableClassLoader; +import org.simantics.scl.compiler.runtime.RuntimeEnvironment; +import org.simantics.scl.compiler.types.TVar; +import org.simantics.scl.compiler.types.Type; +import org.simantics.scl.compiler.types.Types; +import org.simantics.scl.compiler.types.kinds.Kinds; +import org.simantics.scl.compiler.types.util.Polarity; +import org.simantics.scl.compiler.types.util.ProcedureType; +import org.simantics.scl.runtime.function.FunctionImpl1; +import org.simantics.scl.runtime.tuple.Tuple0; + +public class ExpressionEvaluator { + + public static final boolean TRACE_INTERPRETATION_VS_COMPILATION = false; + private static final String COMPUTATION_METHOD_NAME = "main"; + + private final RuntimeEnvironment runtimeEnvironment; + private final String expressionText; + private Expression expression; + private Type expressionType; + + private Type expectedEffect; + private boolean decorateExpression; + private Type expectedType; + private LocalEnvironment localEnvironment; + private LocalStorage localStorage; + private boolean interpretIfPossible = true; + private ExpressionParseMode parseMode = ExpressionParseMode.EXPRESSION; + + public ExpressionEvaluator(RuntimeEnvironment runtimeEnvironment, + String expressionText) { + if(runtimeEnvironment == null) + throw new NullPointerException(); + if(expressionText == null) + throw new NullPointerException(); + this.runtimeEnvironment = runtimeEnvironment; + this.expressionText = expressionText; + } + + public ExpressionEvaluator(RuntimeEnvironment runtimeEnvironment, + LocalStorage localStorage, Expression expression) { + if(runtimeEnvironment == null) + throw new NullPointerException(); + if(expression == null) + throw new NullPointerException(); + this.runtimeEnvironment = runtimeEnvironment; + this.localStorage = localStorage; + this.expressionText = null; + this.expression = expression; + } + + public ExpressionEvaluator expectedEffect(Type expectedEffect) { + this.expectedEffect = expectedEffect; + return this; + } + + public ExpressionEvaluator decorateExpression(boolean decorateExpression) { + this.decorateExpression = decorateExpression; + return this; + } + + public ExpressionEvaluator expectedType(Type expectedType) { + this.expectedType = expectedType; + return this; + } + + /** + * Sets a local environment that can arbitrarily modify the resolving of the expression. + */ + public ExpressionEvaluator localEnvironment(LocalEnvironment localEnvironment) { + this.localEnvironment = localEnvironment; + return this; + } + + /** + * Evaluates the expression by interpretation instead of compilation to bytecode + * if the expression does not contain language constructs that interpretation does + * not support. + */ + public ExpressionEvaluator interpretIfPossible(boolean interpretIfPossible) { + this.interpretIfPossible = interpretIfPossible; + return this; + } + + /** + * Assumes that top level of the expression is similar to the content + * of a do-block. + */ + public ExpressionEvaluator parseAsBlock(boolean parseAsBlock) { + this.parseMode = parseAsBlock ? ExpressionParseMode.BLOCK : ExpressionParseMode.EXPRESSION; + return this; + } + + public ExpressionEvaluator parseModel(ExpressionParseMode parseMode) { + this.parseMode = parseMode; + return this; + } + + private void fillDefaults() { + if(expectedEffect == null) + expectedEffect = Types.metaVar(Kinds.EFFECT); + if(expectedType == null) + expectedType = Types.metaVar(Kinds.STAR); + } + + private static class StoreFunction extends FunctionImpl1 { + final LocalStorage storage; + final String name; + final Type type; + public StoreFunction(LocalStorage storage, String name, Type type) { + this.storage = storage; + this.name = name; + this.type = type; + } + @Override + public Object apply(Object value) { + Type type = Types.closure(this.type.convertMetaVarsToVars()); + storage.store(name, value, type); + return Tuple0.INSTANCE; + } + + @Override + public String toString() { + return "store_" + name; + } + } + + public Object eval() throws SCLExpressionCompilationException { + fillDefaults(); + + final ErrorLog errorLog = new ErrorLog(); + final Environment environment = runtimeEnvironment.getEnvironment(); + + // Parse expression + if(expressionText != null) { + try { + switch(parseMode) { + case BLOCK: { + SCLBlockParser parser = new SCLBlockParser(new StringReader(expressionText)); + parser.parseCommands(); + expression = parser.block; + } break; + case EXPRESSION: { + SCLParserImpl parser = new SCLParserImpl(new StringReader(expressionText)); + expression = (Expression)parser.parseExp(); + } break; + case EQUATION_BLOCK: { + SCLParserImpl parser = new SCLParserImpl(new StringReader(expressionText)); + SCLParserOptions parserOptions = new SCLParserOptions(); + parserOptions.supportEq = true; + parser.setParserOptions(parserOptions); + expression = (Expression)parser.parseEquationBlock(); + } break; + } + } catch(SCLSyntaxErrorException e) { + errorLog.log(e.location, e.getMessage()); + //System.out.println(errorLog.getErrorsAsString()); + throw new SCLExpressionCompilationException(errorLog.getErrors()); + } catch(Exception e) { + errorLog.log(e); + throw new SCLExpressionCompilationException(errorLog.getErrors()); + } + } + + // Store local variables + ArrayList lvTypes = new ArrayList(); + if(expression instanceof EBlock) { + EBlock block = (EBlock)expression; + if(localStorage != null && !(block.getStatements().getLast() instanceof GuardStatement)) { + THashSet localVariables = new THashSet(); + ListIterator it = block.getStatements().listIterator(); + while(it.hasNext()) { + Statement stat = it.next(); + if(!(stat instanceof LetStatement)) + continue; + String variableName; + try { + variableName = ((LetStatement)stat).pattern.getPatternHead().name; + } catch (NotPatternException e) { + continue; + } + localVariables.add(variableName); + } + for(String variableName : localVariables) { + Type type = Types.metaVar(Kinds.STAR); + lvTypes.add(type); + block.addStatement(new GuardStatement(new EApply( + new EExternalConstant( + new StoreFunction(localStorage, variableName, type), + Types.functionE(type, Types.PROC, Types.UNIT)), + new EVar(variableName) + ))); + } + } + if(!(block.getStatements().getLast() instanceof GuardStatement)) + block.addStatement(new GuardStatement(new EConstant(Builtins.TUPLE_CONSTRUCTORS[0]))); + } + + // Elaboration + { + TranslationContext context = new TranslationContext(errorLog, + environment, localEnvironment); + expression = expression.resolve(context); + if(!errorLog.isEmpty()) + throw new SCLExpressionCompilationException(errorLog.getErrors()); + } + + // Apply local environment + if(localEnvironment != null) { + expression = localEnvironment.preDecorateExpression(expression); + ProcedureType procedureType = localEnvironment.decorateExpectedType(expectedType, expectedEffect); + expectedType = procedureType.type; + expectedEffect = procedureType.effect; + } + + // Type checking + { + TypingContext context = new TypingContext(errorLog, environment); + + context.pushEffectUpperBound(expression.location, expectedEffect); + expression = expression.checkType(context, expectedType); + context.popEffectUpperBound(); + + for(Type lvType : lvTypes) + lvType.addPolarity(Polarity.POSITIVE); + + expectedType.addPolarity(Polarity.POSITIVE); + context.solveSubsumptions(expression.location); + if(!errorLog.isEmpty()) + throw new SCLExpressionCompilationException(errorLog.getErrors()); + if(decorateExpression && Types.canonical(expectedEffect) != Types.NO_EFFECTS) { + ExpressionDecorator decorator = + new ToplevelEffectDecorator(errorLog, environment); + expression = expression.decorate(decorator); + } + expression = context.solveConstraints(environment, expression); + expressionType = expression.getType(); + + if(!errorLog.isEmpty()) + throw new SCLExpressionCompilationException(errorLog.getErrors()); + + if(localEnvironment != null) + expression = localEnvironment.postDecorateExpression(expression); + + Type type = expression.getType(); + type = type.convertMetaVarsToVars(); + + for(Type lvType : lvTypes) + lvType.convertMetaVarsToVars(); + + ArrayList varsList = Types.freeVars(type); + expression = expression.closure(varsList.toArray(new TVar[varsList.size()])); + } + + // Initialize code generation + MutableClassLoader classLoader = runtimeEnvironment.getMutableClassLoader(); + String moduleName = classLoader.getFreshPackageName(); + JavaTypeTranslator javaTypeTranslator = new JavaTypeTranslator(environment); + JavaNamingPolicy namingPolicy = new JavaNamingPolicy(moduleName); + + ModuleBuilder moduleBuilder = new ModuleBuilder(namingPolicy, javaTypeTranslator); + + // Simplify + SimplificationContext context = + new SimplificationContext(environment, errorLog, + javaTypeTranslator, DummyJavaReferenceValidator.INSTANCE); + expression = expression.simplify(context); + + if(!errorLog.isEmpty()) + throw new SCLExpressionCompilationException(errorLog.getErrors()); + + if(SCLCompilerConfiguration.SHOW_EXPRESSION_BEFORE_EVALUATION) + System.out.println(expression); + + if(interpretIfPossible) { + // Try to interpret + try { + ExpressionInterpretationContext expressionInterpretationContext = + new ExpressionInterpretationContext(runtimeEnvironment, + new TransientClassBuilder(classLoader, javaTypeTranslator)); + IExpression iexp = expression.toIExpression(expressionInterpretationContext); + if(TRACE_INTERPRETATION_VS_COMPILATION) + System.out.println("INTERPRETED " + expressionText); + if(SCLCompilerConfiguration.SHOW_INTERPRETED_EXPRESSION) + System.out.println("INTERPRETED AS: " + iexp); + return iexp.execute(new Object[expressionInterpretationContext.getMaxVariableId()]); + } catch(UnsupportedOperationException e) { + // This is normal when expression cannot be interpreted. We compile it instead. + } + } + + // Convert to SSA + ModuleWriter mw = new ModuleWriter(namingPolicy.getModuleClassName()); + DecomposedExpression decomposed = + DecomposedExpression.decompose(expression); + + SCLConstant constant = new SCLConstant( + Name.create(moduleName, COMPUTATION_METHOD_NAME), + expression.getType()); + constant.setBase(new JavaStaticMethod( + moduleName, COMPUTATION_METHOD_NAME, + decomposed.effect, + decomposed.typeParameters, + decomposed.returnType, + decomposed.parameterTypes)); + try { + CodeWriter w = mw.createFunction(constant, + decomposed.typeParameters, + decomposed.effect, + decomposed.returnType, + decomposed.parameterTypes); + constant.setDefinition(w.getFunction()); + IVal[] parameterVals = w.getParameters(); + for(int i=0;i classes = moduleBuilder.getClasses(); + + // Load generated code and execute + try { + classLoader.addClasses(classes); + Class clazz = classLoader.loadClass(MutableClassLoader.SCL_PACKAGE_PREFIX + moduleName); + for(ExternalConstant externalConstant : externalConstants) + clazz.getField(externalConstant.fieldName).set(null, externalConstant.value); + for(Method method : clazz.getMethods()) { + if(method.getName().equals(COMPUTATION_METHOD_NAME)) + return ValueFromMethod.getValueFromStaticMethod(method); + } + errorLog.log("Internal compiler error: didn't find method " + + COMPUTATION_METHOD_NAME + " from generated byte code."); + throw new SCLExpressionCompilationException(errorLog.getErrors()); + } catch(ReflectiveOperationException e) { + errorLog.log(e); + throw new SCLExpressionCompilationException(errorLog.getErrors()); + } + } + + public Type getType() { + return expressionType; + } + + public String getExpressionText() { + return expressionText; + } + +}