X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.db.layer0%2Fsrc%2Forg%2Fsimantics%2Fdb%2Flayer0%2Fscl%2FAbstractExpressionCompilationRequest.java;h=b12d0258ede32c4c48e60c287c35250496fa5c77;hb=84d7675119770bf431ff77a5628ca78515fba807;hp=e830b7b8c40782c9289731237e053155ad93b5d3;hpb=969bd23cab98a79ca9101af33334000879fb60c5;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/scl/AbstractExpressionCompilationRequest.java b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/scl/AbstractExpressionCompilationRequest.java index e830b7b8c..b12d0258e 100644 --- a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/scl/AbstractExpressionCompilationRequest.java +++ b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/scl/AbstractExpressionCompilationRequest.java @@ -1,226 +1,234 @@ -package org.simantics.db.layer0.scl; - -import gnu.trove.map.hash.THashMap; - -import java.util.ArrayList; -import java.util.List; - -import org.simantics.db.ReadGraph; -import org.simantics.db.exception.DatabaseException; -import org.simantics.db.exception.RuntimeDatabaseException; -import org.simantics.db.request.Read; -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.EConstant; -import org.simantics.scl.compiler.elaboration.expressions.ELiteral; -import org.simantics.scl.compiler.elaboration.expressions.ESimpleLet; -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.environment.AbstractLocalEnvironment; -import org.simantics.scl.compiler.environment.Environment; -import org.simantics.scl.compiler.environment.LocalEnvironment; -import org.simantics.scl.compiler.errors.CompilationError; -import org.simantics.scl.compiler.runtime.RuntimeEnvironment; -import org.simantics.scl.compiler.top.ExpressionEvaluator; -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.compiler.types.exceptions.MatchException; -import org.simantics.scl.compiler.types.kinds.Kinds; -import org.simantics.scl.compiler.types.util.MultiFunction; -import org.simantics.scl.runtime.SCLContext; -import org.simantics.scl.runtime.function.Function1; -import org.simantics.utils.datastructures.Pair; - -/** - *

This is a base implementation for compiling expressions stored into - * graph. It provides a skeleton and a set of methods that must be - * implemented to get a concrete compilation request. - * - *

The request returns an SCL function with type {@code EvaluationContext -> Result} - * where {@code EvaluationContext} is the type given by {@link #getContextVariableType()} - * and {@code Result} is the type of the expression that can be optionally restricted - * by {@link #getExpectedType(ReadGraph, AbstractExpressionCompilationContext)}. - * - *

Compilation calls {@link #getCompilationContext(ReadGraph)} to read all information - * that is needed about the context of the resource during compilation. The result must - * extend the type {@link AbstractExpressionCompilationContext} including at least information about - * {@link RuntimeEnvironment}. - * - *

Compilation calls {@link #getVariableAccessExpression(ReadGraph, AbstractExpressionCompilationContext, Variable, String)} - * to get access to local environment. The method may return null if the variable is not defined. - * - * @author Hannu Niemistö - * - * @param - */ -public abstract class AbstractExpressionCompilationRequest -implements Read> { - - protected static final Type RESOURCE = Types.con("Simantics/DB", "Resource"); - protected static final Type VARIABLE = Types.con("Simantics/Variables", "Variable"); - protected static Name PROPERTY_VALUE = Name.create("Simantics/Variables", "propertyValue"); - protected static Name VARIABLE_PARENT = Name.create("Simantics/Variables", "variableParent"); - protected static Name FROM_DOUBLE = Name.create("Prelude", "fromDouble"); - protected static Name TO_DOUBLE = Name.create("Prelude", "toDouble"); - protected static Name FROM_DYNAMIC = Name.create("Prelude", "fromDynamic"); - - private static final Type DEFAULT_EXPECTED_EFFECT = Types.union(new Type[] {Types.PROC, Types.READ_GRAPH}); - - /** - * Returns the expression that will be compiled in textual form. - */ - protected abstract String getExpressionText(ReadGraph graph) throws DatabaseException; - /** - * Returns the context that is used for the compilation of the expression. The context - * contains information about available constants and variables. - */ - protected abstract CompilationContext getCompilationContext(ReadGraph graph) throws DatabaseException; - /** - * This should return the SCL type corresponding to generic type parameter {@code EvaluationContext}. - */ - protected abstract Type getContextVariableType(); - /** - * Returns null, if variable {@code name} is not defined. - */ - protected abstract Expression getVariableAccessExpression(ReadGraph graph, - CompilationContext context, Variable contextVariable, String name) throws DatabaseException; - - protected Type getExpectedType(ReadGraph graph, CompilationContext context) throws DatabaseException { - return Types.metaVar(Kinds.STAR); - } - - protected Type getExpectedEffect(ReadGraph graph, CompilationContext context) throws DatabaseException { - return DEFAULT_EXPECTED_EFFECT; - } - - private ExpressionEvaluator prepareEvaluator(final ReadGraph graph, final CompilationContext context, Type expectedType) throws DatabaseException { - final Variable contextVariable = new Variable("context", getContextVariableType()); - LocalEnvironment localEnvironment = new AbstractLocalEnvironment() { - THashMap> precalculatedVariables = new THashMap>(); - @Override - public Expression resolve(Environment environment, String localName) { - Pair precalculatedVariable = precalculatedVariables.get(localName); - if(precalculatedVariable == null) { - try { - Expression value = getVariableAccessExpression(graph, context, contextVariable, localName); - if(value == null) - return null; - Variable variable = new Variable(localName); - precalculatedVariable = Pair.make(variable, value); - precalculatedVariables.put(localName, precalculatedVariable); - } catch (DatabaseException e) { - throw new RuntimeDatabaseException(e); - } - } - return new EVariable(precalculatedVariable.first); - } - @Override - public Expression preDecorateExpression(Expression expression) { - for(Pair precalculatedVariable : precalculatedVariables.values()) - expression = new ESimpleLet(precalculatedVariable.first, precalculatedVariable.second, expression); - return expression; - } - @Override - protected Variable[] getContextVariables() { - return new Variable[] { contextVariable }; - } - }; - - String expressionText = getExpressionText(graph); - return new ExpressionEvaluator(context.runtimeEnvironment, expressionText) - .localEnvironment(localEnvironment) - .expectedType(expectedType) - .parseAsBlock(parseAsBlock()); - } - - protected boolean parseAsBlock() { - return false; - } - - private Function1 eval(ExpressionEvaluator evaluator, ReadGraph graph) throws DatabaseException { - Object oldGraph = SCLContext.getCurrent().put("graph", graph); - try { - return (Function1)evaluator.eval(); - } catch(RuntimeDatabaseException e) { - e.printStackTrace(); - if(e.getCause() instanceof DatabaseException) - throw (DatabaseException)e.getCause(); - else - throw e; - } catch (SCLExpressionCompilationException e) { - StringBuilder b = new StringBuilder(); - b.append("Couldn't compile '"); - b.append(evaluator.getExpressionText()); - b.append("':\n"); - StringBuilder b2 = new StringBuilder(); - for(CompilationError error : e.getErrors()) { - b2.append(error.description); - b2.append('\n'); - } - System.err.println(b.toString() + b2.toString()); - throw new SCLDatabaseException(b.toString()+b2.toString(), b2.toString(), e.getErrors()); - } catch(Throwable e) { - // Should not happen! - e.printStackTrace(); - throw new RuntimeException(e); - } finally { - SCLContext.getCurrent().put("graph", oldGraph); - } - } - - public List getExpressionEffects(final ReadGraph graph) throws DatabaseException { - CompilationContext context = getCompilationContext(graph); - Type type = Types.metaVar(Kinds.STAR); - eval(prepareEvaluator(graph, context, type), graph); - - try { - MultiFunction mfun = Types.matchFunction(type, 1); - ArrayList concreteEffects = new ArrayList(); - mfun.effect.collectConcreteEffects(concreteEffects); - return concreteEffects; - } catch(MatchException e) { - // Should not happen! - e.printStackTrace(); - throw new RuntimeException(e); - } - } - - @SuppressWarnings("unchecked") - @Override - public Function1 perform(final ReadGraph graph) throws DatabaseException { - CompilationContext context = getCompilationContext(graph); - return eval(prepareEvaluator(graph, context, getExpectedType(graph, context)), graph); - } - - protected static Expression getProperty(Environment environment, Expression variable, String propertyName, Type type) { - return new EApply( - new EConstant(environment.getValue(FROM_DYNAMIC), type), - new EApply( - new EConstant(environment.getValue(PROPERTY_VALUE), Types.DYNAMIC), - variable, - new ELiteral(new StringConstant(propertyName)))); - } - - protected static Expression getPropertyFlexible(Environment environment, Expression variable, String propertyName, Type type) { - return makeTypeFlexible(environment, getProperty(environment, variable, propertyName, type), type); - } - - protected static Expression makeTypeFlexible(Environment environment, Expression base, Type originalType) { - if(originalType.equals(Types.DOUBLE)) - return new EApply( - new EConstant(environment.getValue(FROM_DOUBLE)), - base); - else if(originalType.equals(Types.FLOAT)) - return new EApply( - new EConstant(environment.getValue(FROM_DOUBLE)), - new EApply( - new EConstant(environment.getValue(TO_DOUBLE), Types.FLOAT), - base)); - else - return base; - } -} +package org.simantics.db.layer0.scl; + +import gnu.trove.map.hash.THashMap; + +import java.util.ArrayList; +import java.util.List; + +import org.simantics.databoard.Bindings; +import org.simantics.db.ReadGraph; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.exception.RuntimeDatabaseException; +import org.simantics.db.request.Read; +import org.simantics.layer0.Layer0; +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.EConstant; +import org.simantics.scl.compiler.elaboration.expressions.ELiteral; +import org.simantics.scl.compiler.elaboration.expressions.ESimpleLet; +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.environment.AbstractLocalEnvironment; +import org.simantics.scl.compiler.environment.Environment; +import org.simantics.scl.compiler.environment.LocalEnvironment; +import org.simantics.scl.compiler.errors.CompilationError; +import org.simantics.scl.compiler.runtime.RuntimeEnvironment; +import org.simantics.scl.compiler.top.ExpressionEvaluator; +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.compiler.types.exceptions.MatchException; +import org.simantics.scl.compiler.types.kinds.Kinds; +import org.simantics.scl.compiler.types.util.MultiFunction; +import org.simantics.scl.runtime.SCLContext; +import org.simantics.scl.runtime.function.Function1; +import org.simantics.utils.datastructures.Pair; + +/** + *

This is a base implementation for compiling expressions stored into + * graph. It provides a skeleton and a set of methods that must be + * implemented to get a concrete compilation request. + * + *

The request returns an SCL function with type {@code EvaluationContext -> Result} + * where {@code EvaluationContext} is the type given by {@link #getContextVariableType()} + * and {@code Result} is the type of the expression that can be optionally restricted + * by {@link #getExpectedType(ReadGraph, AbstractExpressionCompilationContext)}. + * + *

Compilation calls {@link #getCompilationContext(ReadGraph)} to read all information + * that is needed about the context of the resource during compilation. The result must + * extend the type {@link AbstractExpressionCompilationContext} including at least information about + * {@link RuntimeEnvironment}. + * + *

Compilation calls {@link #getVariableAccessExpression(ReadGraph, AbstractExpressionCompilationContext, Variable, String)} + * to get access to local environment. The method may return null if the variable is not defined. + * + * @author Hannu Niemistö + * + * @param + */ +public abstract class AbstractExpressionCompilationRequest +implements Read> { + + protected static final Type RESOURCE = Types.con("Simantics/DB", "Resource"); + protected static final Type VARIABLE = Types.con("Simantics/Variables", "Variable"); + protected static Name PROPERTY_VALUE = Name.create("Simantics/Variables", "propertyValue"); + protected static Name VARIABLE_PARENT = Name.create("Simantics/Variables", "variableParent"); + protected static Name FROM_DOUBLE = Name.create("Prelude", "fromDouble"); + protected static Name TO_DOUBLE = Name.create("Prelude", "toDouble"); + protected static Name FROM_DYNAMIC = Name.create("Prelude", "fromDynamic"); + + private static final Type DEFAULT_EXPECTED_EFFECT = Types.union(new Type[] {Types.PROC, Types.READ_GRAPH}); + + /** + * Returns the expression that will be compiled in textual form. + */ + protected abstract String getExpressionText(ReadGraph graph) throws DatabaseException; + /** + * Returns the context that is used for the compilation of the expression. The context + * contains information about available constants and variables. + */ + protected abstract CompilationContext getCompilationContext(ReadGraph graph) throws DatabaseException; + /** + * This should return the SCL type corresponding to generic type parameter {@code EvaluationContext}. + */ + protected abstract Type getContextVariableType(); + /** + * Returns null, if variable {@code name} is not defined. + */ + protected abstract Expression getVariableAccessExpression(ReadGraph graph, + CompilationContext context, Variable contextVariable, String name) throws DatabaseException; + + protected Type getExpectedType(ReadGraph graph, CompilationContext context) throws DatabaseException { + return Types.metaVar(Kinds.STAR); + } + + protected Type getExpectedEffect(ReadGraph graph, CompilationContext context) throws DatabaseException { + return DEFAULT_EXPECTED_EFFECT; + } + + private ExpressionEvaluator prepareEvaluator(final ReadGraph graph, final CompilationContext context, Type expectedType) throws DatabaseException { + final Variable contextVariable = new Variable("context", getContextVariableType()); + LocalEnvironment localEnvironment = new AbstractLocalEnvironment() { + THashMap> precalculatedVariables = new THashMap>(); + @Override + public Expression resolve(Environment environment, String localName) { + Pair precalculatedVariable = precalculatedVariables.get(localName); + if(precalculatedVariable == null) { + try { + Expression value = getVariableAccessExpression(graph, context, contextVariable, localName); + if(value == null) + return null; + Variable variable = new Variable(localName); + precalculatedVariable = Pair.make(variable, value); + precalculatedVariables.put(localName, precalculatedVariable); + } catch (DatabaseException e) { + throw new RuntimeDatabaseException(e); + } + } + return new EVariable(precalculatedVariable.first); + } + @Override + public Expression preDecorateExpression(Expression expression) { + for(Pair precalculatedVariable : precalculatedVariables.values()) + expression = new ESimpleLet(precalculatedVariable.first, precalculatedVariable.second, expression); + return expression; + } + @Override + protected Variable[] getContextVariables() { + return new Variable[] { contextVariable }; + } + }; + + String expressionText = getExpressionText(graph); + return new ExpressionEvaluator(context.runtimeEnvironment, expressionText) + .localEnvironment(localEnvironment) + .expectedType(expectedType) + .parseAsBlock(parseAsBlock()); + } + + protected boolean parseAsBlock() { + return false; + } + + private Function1 eval(ExpressionEvaluator evaluator, ReadGraph graph) throws DatabaseException { + Object oldGraph = SCLContext.getCurrent().put("graph", graph); + try { + return (Function1)evaluator.eval(); + } catch(RuntimeDatabaseException e) { + e.printStackTrace(); + if(e.getCause() instanceof DatabaseException) + throw (DatabaseException)e.getCause(); + else + throw e; + } catch (SCLExpressionCompilationException e) { + StringBuilder b = new StringBuilder(); + b.append("Couldn't compile '"); + b.append(evaluator.getExpressionText()); + b.append("':\n"); + StringBuilder b2 = new StringBuilder(); + for(CompilationError error : e.getErrors()) { + b2.append(error.description); + b2.append('\n'); + } + System.err.println(b.toString() + b2.toString()); + throw new SCLDatabaseException(b.toString()+b2.toString(), b2.toString(), e.getErrors()); + } catch(Throwable e) { + // Should not happen! + e.printStackTrace(); + throw new RuntimeException(e); + } finally { + SCLContext.getCurrent().put("graph", oldGraph); + } + } + + public List getExpressionEffects(final ReadGraph graph) throws DatabaseException { + CompilationContext context = getCompilationContext(graph); + Type type = Types.metaVar(Kinds.STAR); + eval(prepareEvaluator(graph, context, type), graph); + + try { + MultiFunction mfun = Types.matchFunction(type, 1); + ArrayList concreteEffects = new ArrayList(); + mfun.effect.collectConcreteEffects(concreteEffects); + return concreteEffects; + } catch(MatchException e) { + // Should not happen! + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + @SuppressWarnings("unchecked") + @Override + public Function1 perform(final ReadGraph graph) throws DatabaseException { + CompilationContext context = getCompilationContext(graph); + return eval(prepareEvaluator(graph, context, getExpectedType(graph, context)), graph); + } + + protected static Expression getProperty(Environment environment, Expression variable, String propertyName, Type type) { + return new EApply( + new EConstant(environment.getValue(FROM_DYNAMIC), type), + new EApply( + new EConstant(environment.getValue(PROPERTY_VALUE), Types.DYNAMIC), + variable, + new ELiteral(new StringConstant(propertyName)))); + } + + protected static Expression getPropertyFlexible(Environment environment, Expression variable, String propertyName, Type type) { + return makeTypeFlexible(environment, getProperty(environment, variable, propertyName, type), type); + } + + protected static Expression makeTypeFlexible(Environment environment, Expression base, Type originalType) { + if(originalType.equals(Types.DOUBLE)) + return new EApply( + new EConstant(environment.getValue(FROM_DOUBLE)), + base); + else if(originalType.equals(Types.FLOAT)) + return new EApply( + new EConstant(environment.getValue(FROM_DOUBLE)), + new EApply( + new EConstant(environment.getValue(TO_DOUBLE), Types.FLOAT), + base)); + else + return base; + } + + protected static String resolveExpectedValueType(ReadGraph graph, org.simantics.db.layer0.variable.Variable context) throws DatabaseException { + Layer0 L0 = Layer0.getInstance(graph); + String valueType = graph.getPossibleRelatedValue(context.getPredicateResource(graph), L0.RequiresValueType, Bindings.STRING); + return valueType; + } +}